SpringSecurity使用数据库的用户数据做认证
创始人
2024-06-02 19:00:00
0

SpringSecurity使用UserDetailsService 接口来获取用户信息UserDetails ,根据UserDetails 的 getPassword() 方法,判断用户名密码是否正确。大致流程如下:

框架默认情况使用InMemoryUserDetailsManager ,即把用户信息存到内存里,一般在开发阶段使用。通过以下配置添加默认测试用户:

spring:security:user:name: "user"password: "123"

要使用自己的用户数据源,有三个地方要改,也就是上图标绿的部分:

  1. 自定义UserDetailsService替换掉 InMemoryUserDetailsManager,实现UserDetails loadUserByUsername(String username)方法。

  1. 如果要额外保存用户信息,例如手机号、生日等,就自定义UserDetails 在loadUserByUsername 中返回。

  1. 提供一个PasswordEncoder Bean。

自定义UserDetailsService

InMemoryUserDetailsManager 初始化的位置是在UserDetailsServiceAutoConfiguration 配置文件里。这个配置有一些生效条件:

@ConditionalOnMissingBean(value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,AuthenticationManagerResolver.class },...
public class UserDetailsServiceAutoConfiguration {@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties,ObjectProvider passwordEncoder) {SecurityProperties.User user = properties.getUser();List roles = user.getRoles();return new InMemoryUserDetailsManager(User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build());}
}

所以,我们提供UserDetailsService Bean之后,配置就会自动失效。这是实例,要注意接口约定loadUserByUsername方法不能返回null,所以找不到用户时要抛UsernameNotFoundException 异常。

public class CustomUserDetailsService implements UserDetailsService {@AutowiredUserRepository repository;/*** Returns: a fully populated user record (never null)* Throws: UsernameNotFoundException – if the user could not be found or the user has no GrantedAuthority*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Optional userOptional = repository.findOne((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("username"), username));if (userOptional.isEmpty()) {throw new UsernameNotFoundException(username);}User user = userOptional.get();return new CustomUser(user.getUsername(), user.getPassword(), List.of());}
}

自定义UserDetails

UserDetails 用来存放身份认证和权限认证的信息,也可以额外存我们需要的信息。我这里直接继承SpringSecurity自带给JDBC用的User类型。

public class CustomUser extends User {public DoitUser(String username, String password, Collection authorities) {super(username, password, authorities);}public DoitUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) {super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);}
}

自定义PasswordEncoder

由于DaoAuthenticationProvider 使用PasswordEncoder 对用户密码进行校验。

if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {this.logger.debug("Failed to authenticate since password does not match stored value");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}

所以我们要提供一个PasswordEncoder 实现,可以用BCryptPasswordEncoder 。

@Bean
public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}

另外,使用PasswordEncoder 别忘了在用户注册的时候,把用户密码先加密再保存!

public User register(@RequestBody User user) {user.setPassword(passwordEncoder.encode(user.getPassword()));...
}

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...