清晰搞懂Spring Security的登录认证( 五 )

到这里问题不大的话应该你的idea就是下面图片里面的结构啦(因为我是已经敲完代码再做的笔记,所以空的很多地方是因为我盖住了)

清晰搞懂Spring Security的登录认证

文章插图
 
3.2、实现UserDetailsService接口这个是和视频中的顺序是一样的,原本是在考虑着按照流程图来先写登录接口再写这一块比对账号密码细节的,但是最终还是决定按照视频的顺序来 。
因为最核心的其实就是在这一块,在接口中有一个 loadUserByUsername 方法需要我们进行重写,这个就是在数据库中拿数据出来对比而不是使用Spring Security自生成的随机的账号密码进行对比,可能也会更直观一点 。
3.2.1、具体实现1、大致实现步骤
  • 注入UserMapper 。因为这里需要操作数据库,因此我们需要引入登录时需要的Mapper对数据进行操作;
  • 使用 MP 获取用户数据,若查询不到则抛出异常
  • 注意的是 loadUserByUsername 方法返回的数据类型为 UserDetails ,因此需要将查询到的数据 封装 到该数据类型中
@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名查询用户信息LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.eq(User::getUserName,username);User user = userMapper.selectOne(wrapper);//如果查询不到数据就通过抛出异常来给出提示if(ObjectUtils.isEmpty(user)){throw new RuntimeException("用户名或密码错误");}//封装成UserDetails对象返回,其中LoginUser为UserDetails的实现类return new LoginUser(user);}}2、为实现 loadUserByUsername 方法实现 UserDetails 接口。仔细翻看我们会发现UserDetails其实只是一个接口,因此我们需要自定义一个实现类来实现该接口从而达到我们的需求 。
@Data@NoArgsConstructor@AllArgsConstructorpublic class LoginUser implements UserDetails {/** 使用构造方法初始化 */private User user;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUserName();}/** 下面的方法暂时全部都让他们返回true */@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}}3、初见成效 。这时其实我们就可以利用自带的登录界面和接口,输入账号密码和数据库中数据对比进行登录了 。
清晰搞懂Spring Security的登录认证

文章插图
 
【清晰搞懂Spring Security的登录认证】但是如果要测试,并且如果想让用户的密码是明文存储,需要在密码前加{noop},如密码为 1234 ,那在数据库中的数据就得为 {noop}1234 ,这与默认使用的PasswordEncoder有关 。
清晰搞懂Spring Security的登录认证

文章插图
 
3.2.2、密码问题解决通过上面的测试我们发现,这框架是个什么玩意,密码都搞这么麻烦,而且我们实际项目中也不会将密码明文存储在数据库中对吧,这要是让哪个老六拿到手自己岂不是成大怨种了,因此我们要改进!
在Spring Security中默认使用的PasswordEncoder要求数据库中的密码格式为 {id}password ,框架会根据前面的id去判断密码的加密模式,我们上面的 {noop}1234 也是属于这种格式,其中的 noop 就标明着这个密码是以明文的形式进行存储的,就会直接使用后面的 1234 当作密码 。
而一般我们会使用Spring Security为我们提供的 BCryptPasswordEncoder ,其操作也很简单,我们只需要把 BCryptPasswordEncoder 对象注入Spring容器中计科,Spring Security就会使用我们自定义的 PasswordEncoder 进行密码校验 。
那怎么将其加入到Spring容器中呢?我们可以定义一个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承
WebSecurityConfigurerAdapter 。
@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 将BCryptPasswordEncoder加入到容器中**/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}}这里可以用测试类对 BCryptPasswordEncoder 进行测试,并且拿到我们密码加密之后的密文,值得注意的是,即使是同一个密码,两次加密出来的密文很有可能并不是相同的,这是因为该加密方式会添加一个 随机盐 一起进行加密 。


推荐阅读