文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-01-04 12:02

关注

前言

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

本章内容

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

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

image-20221230141214863

public interface UserDetailsService {
   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() {
        ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Roles roles : getRolesList()) {
            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这个函数就行了。看一下有哪些方法调用了这个函数?"

image-20221230142516975

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

那我们也可以效仿他的方式,在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) // 认证成功后访问
         .permitAll() // 白名单
         .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的时候, 我们一般都要看看他的类族是怎样的, 结果发现可以偷懒的地方, 也就是表结构的位置

image-20221230010540110

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生成的, 简单"

启动, 访问 崩了

image-20221230012540098

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

分析源码看看

脱敏为什么没有生效?

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

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

image-20221230015259596

image-20221230015347390

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

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

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

image-20221230015616746

完美~~~

总结

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

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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯