开发者

详解Spring Security怎么从数据库加载我们的用户

目录
  • 本章内容
  • 如何从数据库中读取用户对象?
    • 脱敏为什么没有生效?
  • 总结

    本章内容

    • 如何从数据库中读取用户对象
    • 源码分析

    如何从数据库中读取用户对象?

    1前面我们分析认证的时候就会发现他在DaoAuthenticationProvider中就已经有从数据库中获取用户名和密码的。

    详解Spring Security怎么从数据库加载我们的用户

    public interface UserDetailsService {
    http://www.devze.com   UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
    }
    

    意思是说我们只要重写这个类,就可以从我们所想要的地方获取用户信息。

    那我们重写吧

    public class MyBATisUserDetailsService implements UserDetailsService, UserDetailsPasswordService {
    	@Resource
    	private UsersMapper usersMapper;
    	@Override
    	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    		Users users = usersMapper.loadUserByUsername(username);
    		if (null == users) {
    			throw new UsernameNotFoundException(username);
    		}
    		return users;
    	}
    	@Override
    	public UserDetails updatePassword(UserDetails user, String newpassword) {
    		if (user instanceof Users users) {
    			users.setPassword(newPassword);
    			usersMapper.updateByPrimaryKeySelective(users);
    		}
    		return user;
    	}
    }
    

    发现这里需要把spring security的users对象转换成我们所需要的users对象。比较麻烦。

    小白: "为什么不直接使用spring security那里面的users对象呢?"

    小黑: "我们自己实现users对象的话就可以在自定义。特别是权限这边,我们肯定是需要自定义users对象的,不能直接使用其内部的对象。"

    然后我们直接继承UserDetails接口,然后自定义一些我们所需要的属性。说白了就是从内置users对象中拷贝一些代码出来。

    小黑: "这里我故意留下了一个坑,如果你真的从内置的users对象里的话,你会发现一个问题。"

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class Users implements Serializable, UserDetails {
        private Long id;
        private String username;
        private String password;
        private Boolean enabled;
        private Boolean accountnonexpired;
        private Boolean accountnonlocked;
        private Boolean credentialsnonexpired;
        private List<Roles> rolesList;
        private static final long serialVersionUID = 1L;
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities(python) {
            ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
            for (Roles roles : getRolesLis编程客栈t()) {
                authorities.add(new SimpleGrantedAuthority(roles.getRole()));
            }
            return authorities;
        }
        @Override
        public boolean isAccountNonExpired() {
            return accountnonexpired;
        }
        @Override
        public boolean isAccountNonLocked() {
            return accountnonlocked;
        }
        @Override
        public boolean isCredentialsNonExpired() {
            return credentialsnonexpired;
        }
        @Override
        public boolean isEnabled() {
            return enabled;
        }
    }
    

    小白: "代码定义好了,但现在你要怎么加入到spring security中呢?让spring security主动调用我们自定义的类呢?"

    小黑: "我们要使用自定义的类的话,无非是在DaoAuthenticationProvider里面设置。所以我们只要找到setUserDetailsService这个函数就行了。看一下有哪些方法调用了这个函数?"

    详解Spring Security怎么从数据库加载我们的用户

    看了一下发现这边调用了setUserDetailsService方法的。前面的UserDetailsService是对象是通过spring bean上下文面拿出来的。

    那我们也可以效开发者_JAVA入门仿他的方式,在spring bean上添加我们的UserDetailsService Bean。

    @Bean
    public UserDetailsService userDetailsService() {
       return new MybatisUserDetailsService();
    }
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
       return httpSecurity.authorizeHttpRequests()
             .anyRequest().authenticated()
             .and()
             .formLogin()
             .defaultSuccessUrl("/hello", true) // 认证成功后访问
             .p编程ermitAll() // 白名单
             .and()
             .logout()
             .logoutUrl("/logout")
             .logoutSuccessUrl("/login") // 注销成功后访问
             .clearAuthentication(true)
             .invalidateHttpSession(true)
             .and()
             .build();
    }
    
    @GetMapping("hello")
    public HashMap<String, Object> hello(Authentication authentication) {
       HashMap<String, Object> map = new HashMap<>();
       Object principal = authentication.getPrincipal();
       String name = authentication.getName();
       map.put("principal", principal);
       map.put("name", name);
       return map;
    }
    

    小白: "停一下, 你数据库表结构呢? "

    小黑: "我忘了, 我找找在哪可以抄"

    在分析UserDetailsService的时候, 我们一般都要看看他的类族是怎样的, 结果发现可以偷懒的地方, 也就是表结构的位置

    详解Spring Security怎么从数据库加载我们的用户

    create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);
    create table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username));
    create unique index ix_auth_username on authorities (username,authority);
    

    这段sql要改改, 否则mysql无法执行

    结果发现, 就这...

    我还不如自己设计呢

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    -- ----------------------------
    -- Table structure for authorities
    -- ----------------------------
    DROP TABLE IF EXISTS `authorities`;
    CREATE TABLE `authorities`  (
      `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,编程
      `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
      UNIQUE INDEX `ix_auth_username`(`username` ASC, `authority` ASC) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
    -- ----------------------------
    -- Table structure for users
    -- ----------------------------
    DROP TABLE IF EXISTS `users`;
    CREATE TABLE `users`  (
      `id` bigint NOT NULL AUTO_INCREMENT,
      `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
      `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `enabled` bit(1) NULL DEFAULT NULL,
      `accountNonExpired` bit(1) NULL DEFAULT b'1',
      `accountNonLocked` bit(1) NULL DEFAULT b'1',
      `credentialsNonExpired` bit(1) NULL DEFAULT b'1',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
    SET FOREIGN_KEY_CHECKS = 1;
    

    注意, 这里仅仅只是为了玩耍, 而非企业, 在企业中, 肯定不是这么设计的, 一般根据 RBAC 设计

    小白: "这样就完成了?"

    小黑: "完成了, 其他代码都是mybatis生成的, 简单"

    启动, 访问 崩了

    详解Spring Security怎么从数据库加载我们的用户

    等下, spring security脱敏呢? 为什么这里可以输出密码?

    分析源码看看

    脱敏为什么没有生效?

    通过源码分析,脱敏应该是认证成功之后的事情

    大概看了下源码, ProviderManager有脱敏, 但是为什么不生效呢?

    详解Spring Security怎么从数据库加载我们的用户

    详解Spring Security怎么从数据库加载我们的用户

    看这代码的意思, 是要我们在 Users 类上多添加一个接口CredentialsContainer

    public class Users implements Serializable, UserDetails, CredentialsContainer {
    	@Override
    	public void eraseCredentials() {
            // 设置 password 为 null
    		this.password = null;
    	}
    }
    

    行, 前面的坑补上了。再试试

    详解Spring Security怎么从数据库加载我们的用户

    完美~~~

    总结

    记住UserDetailsService怎么自定义, 注意自定义的User需要实现哪些接口, 还有脱敏问题

    以上就是详解Spring Security怎么从数据库加载我们的用户的详细内容,更多关于Spring Security数据库加载用户的资料请关注我们其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜