当前位置:   article > 正文

Spring Security怎么从数据库加载我们的用户?_spring security loadbyusername

spring security loadbyusername

前言

水着水着我们就水到了比较重要的地方。

本章内容

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

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

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

  1. public interface UserDetailsService {
  2. UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
  3. }
  4. 复制代码

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

那我们重写吧

  1. public class MybatisUserDetailsService implements UserDetailsService, UserDetailsPasswordService {
  2. @Resource
  3. private UsersMapper usersMapper;
  4. @Override
  5. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  6. Users users = usersMapper.loadUserByUsername(username);
  7. if (null == users) {
  8. throw new UsernameNotFoundException(username);
  9. }
  10. return users;
  11. }
  12. @Override
  13. public UserDetails updatePassword(UserDetails user, String newPassword) {
  14. if (user instanceof Users users) {
  15. users.setPassword(newPassword);
  16. usersMapper.updateByPrimaryKeySelective(users);
  17. }
  18. return user;
  19. }
  20. }
  21. 复制代码

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

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

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

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

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

  1. @Data
  2. @Builder
  3. @AllArgsConstructor
  4. @NoArgsConstructor
  5. public class Users implements Serializable, UserDetails {
  6. private Long id;
  7. private String username;
  8. private String password;
  9. private Boolean enabled;
  10. private Boolean accountnonexpired;
  11. private Boolean accountnonlocked;
  12. private Boolean credentialsnonexpired;
  13. private List<Roles> rolesList;
  14. private static final long serialVersionUID = 1L;
  15. @Override
  16. public Collection<? extends GrantedAuthority> getAuthorities() {
  17. ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
  18. for (Roles roles : getRolesList()) {
  19. authorities.add(new SimpleGrantedAuthority(roles.getRole()));
  20. }
  21. return authorities;
  22. }
  23. @Override
  24. public boolean isAccountNonExpired() {
  25. return accountnonexpired;
  26. }
  27. @Override
  28. public boolean isAccountNonLocked() {
  29. return accountnonlocked;
  30. }
  31. @Override
  32. public boolean isCredentialsNonExpired() {
  33. return credentialsnonexpired;
  34. }
  35. @Override
  36. public boolean isEnabled() {
  37. return enabled;
  38. }
  39. }
  40. 复制代码

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

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

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

那我们也可以效仿他的方式,在spring bean上添加我们的UserDetailsService Bean。

  1. @Bean
  2. public UserDetailsService userDetailsService() {
  3. return new MybatisUserDetailsService();
  4. }
  5. 复制代码
  1. @Bean
  2. public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
  3. return httpSecurity.authorizeHttpRequests()
  4. .anyRequest().authenticated()
  5. .and()
  6. .formLogin()
  7. .defaultSuccessUrl("/hello", true) // 认证成功后访问
  8. .permitAll() // 白名单
  9. .and()
  10. .logout()
  11. .logoutUrl("/logout")
  12. .logoutSuccessUrl("/login") // 注销成功后访问
  13. .clearAuthentication(true)
  14. .invalidateHttpSession(true)
  15. .and()
  16. .build();
  17. }
  18. 复制代码
  1. @GetMapping("hello")
  2. public HashMap<String, Object> hello(Authentication authentication) {
  3. HashMap<String, Object> map = new HashMap<>();
  4. Object principal = authentication.getPrincipal();
  5. String name = authentication.getName();
  6. map.put("principal", principal);
  7. map.put("name", name);
  8. return map;
  9. }
  10. 复制代码

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

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

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

  1. create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);
  2. 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));
  3. create unique index ix_auth_username on authorities (username,authority);
  4. 复制代码

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

结果发现, 就这...

我还不如自己设计呢

  1. SET NAMES utf8mb4;
  2. SET FOREIGN_KEY_CHECKS = 0;
  3. -- ----------------------------
  4. -- Table structure for authorities
  5. -- ----------------------------
  6. DROP TABLE IF EXISTS `authorities`;
  7. CREATE TABLE `authorities` (
  8. `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  9. `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  10. UNIQUE INDEX `ix_auth_username`(`username` ASC, `authority` ASC) USING BTREE
  11. ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
  12. -- ----------------------------
  13. -- Table structure for users
  14. -- ----------------------------
  15. DROP TABLE IF EXISTS `users`;
  16. CREATE TABLE `users` (
  17. `id` bigint NOT NULL AUTO_INCREMENT,
  18. `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  19. `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  20. `enabled` bit(1) NULL DEFAULT NULL,
  21. `accountNonExpired` bit(1) NULL DEFAULT b'1',
  22. `accountNonLocked` bit(1) NULL DEFAULT b'1',
  23. `credentialsNonExpired` bit(1) NULL DEFAULT b'1',
  24. PRIMARY KEY (`id`) USING BTREE
  25. ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
  26. SET FOREIGN_KEY_CHECKS = 1;
  27. 复制代码

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

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

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

启动, 访问 崩了

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

分析源码看看

脱敏为什么没有生效?

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

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

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

  1. public class Users implements Serializable, UserDetails, CredentialsContainer {
  2. @Override
  3. public void eraseCredentials() {
  4. // 设置 password 为 null
  5. this.password = null;
  6. }
  7. }
  8. 复制代码

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

完美~~~

总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/481597
推荐阅读
相关标签
  

闽ICP备14008679号