在实际开发过程中,我们的用户数据肯定不能存储在内存中。业务数据,一般都需要进行持久化处理。所以我们应该将用户数据放到数据库中存储。
在《重要概念及部分流程》中,提到过 Spring Security 支持三种用户数据存储方式:
- 基于内存的简单存储 (
In-Memory Authentication
) - 关系型数据库存储 (
JDBC Authentication
) - 自定义数据存储 (
UserDetailsService
)
并且已经了解到,DaoAuthenticationProvider
从 UserDetailsService
调用方法获取 UserDetails
。
所以,我们需要扩展UserDetailService
和UserDetails
。
创建项目
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
复制代码
UserDetails
我们首先来实现UserDetails
。在实际场景中,我们不可能只存储用户名和密码,总会有一些其他的扩展字段。
@Data
public class MyUserDetails implements UserDetails {
private Long id;
private String phone;
private String username;
private String password;
/** 状态 (1:正常;0:禁用) */
private Integer accountStatus;
/** 角色集合 */
private List<AuthRole> roleList;
/** 修改为 Security 可识别 的GrantedAuthority */
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roleList.stream()
.map(authRole -> new SimpleGrantedAuthority(authRole.getName()))
.collect(Collectors.toList());
}
@Override
public String getPassword() { return this.password;}
@Override
public String getUsername() { return this.username;}
/** 账户是否未过期 */
@Override
public boolean isAccountNonExpired() { return true; }
/** 是否未被锁定 */
@Override
public boolean isAccountNonLocked() { return true; }
/** 密码是否未过期 */
@Override
public boolean isCredentialsNonExpired() { return true;m }
/** 账户是否可用 */
@Override
public boolean isEnabled() { return accountStatus.equals(1); }
复制代码
UserDetailService
Spring Security 通过 UserDetailService#loadUserByUsername
获取UserDetails
。
此处的UserRepository
、RoleRepository
均是模拟持久层操作。
模拟配置的两个用户:
AuthUser zhangsan = new AuthUser()
.setId(1L)
.setUsername("张三")
.setPassword(bCryptPasswordEncoder.encode("123456"))
.setAccountStatus(1);
AuthUser lisi = new AuthUser()
.setId(2L)
.setUsername("李四")
.setPassword(bCryptPasswordEncoder.encode("654321"))
.setAccountStatus(1);
复制代码
UserDetailsService
@Service
public class MyUserDetailServiceImpl implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AuthUser authUser = userRepository.getUserByUsername(username);
List<AuthRole> roleList = roleRepository.getRoleListByUserId(authUser.getId());
MyUserDetails userDetails = new MyUserDetails();
BeanUtils.copyProperties(authUser,userDetails);
userDetails.setRoleList(roleList);
return userDetails;
}
}
复制代码
对了,因为在做认证时,会用PasswordEncoder
验证密码。所以直接先注入了一个Bean,用来加密密码。
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
复制代码
登陆
访问http://localhost:3344/actuator
,被拦截到登陆界面
我们先随便输入用户名密码,其验证不通过。
接着,我们输入预配置的用户名密码。
登陆成功,并且请求接口有数据返回了。
但这还有一点,不符合我们的日常的用法。这个登陆请求用的form-data
格式,而且我们登陆的时候一般都有验证码之类附加验证。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END