UserDetailsService
前文已经认识了 UserDetails,但是 Spring Securit 是从哪获取的用户呢?以及如何添加用户、修改用户?这就是 UserDetailsService 的职责所在了。
先看一下 UserDetailsService 的定义,它只有一个方法,通过 username 来获取用户,返回值是 UserDetails 类型对象。当找不到对应的用户时,会抛出 UsernameNotFoundException。

我们要做的就是实现 loadUserByUsername() 方法,通过读取数据库等方法获取到用户信息。
先实现一个功能简单的 UserDetailsService,功能类似于前面的 InMemoryUserDetailsManager。在实现 UserDetailsService 之前,还需要先实现 UserDetails。
实现 UserDetails,用于创建 UserDetails 对象。
// User.java
public class User implements UserDetails {
private final String username;
private final String password;
private final String authority;
public User(String username, String password, String authority) {
this.username = username;
this.password = password;
this.authority = authority;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(() -> authority);
}
}
实现 UserDetailsService,用于保存、获取用户。
// InMemoryUserDetailsService.java
public class InMemoryUserDetailsService implements UserDetailsService {
// 把用户保存在对象属性中
private final List<UserDetails> users;
public InMemoryUserDetailsService(List<UserDetails> users) {
this.users = users;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从对象的 users 属性中查找、比对用户
return users.stream()
.filter(user -> user.getUsername().equals(username))
.findFirst()
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
}
配置自定义的 InMemoryUserDetailsService,一个简单但完整的示例就完成了。
// SecurityConfig.java
@Configuration
public class SecurityConfig {
@Bean
UserDetailsService userDetailsService() {
// 创建一个用户
UserDetails user = new User("user2", "123456", "read");
UserDetailsService service = new InMemoryUserDetailsService(List.of(user));
return service;
}
@Bean
PasswordEncoder passwordEncoder() {
// 使用 NoOpPasswordEncoder 作为密码编码器
return NoOpPasswordEncoder.getInstance();
}
}
UserDetailsManager
很多时候,应用中还需要创建、修改、删除用户的功能,Spring Security 就提供了这样一个接口 UserDetailsManager,通过方法名能很清除地明白各个方法的作用。

实际工作中,大部分情况用户都是保存在数据中的,Spring Security 提供了一个通过数据库实现 UserDetailsManager 的类 JdbcUserDetailsManager。它默认(可以自定义)需要两个表:users 表和 authorities 表,users 表至少有 username、password、enabled 三个列,用来保存用户的信息,authorities 表至少有 username、authority 两个列来保存授权,并与用户关联。在这里可以查看 Spring Boot 如何连接 MySQL 数据库。
users 表

authorities 表

数据库创建完毕后,添加一个用户,然后在 authorities 表中添加一条与用户对应的 authority 数据。这些准备工作完成后,就可以配置 JdbcUserDetailsManager 了。
// SecurityConfig.java
@Configuration
public class SecurityConfig {
// 注入 DataSource
@Resource
private DataSource dataSource;
@Bean
UserDetailsService userDetailsService() {
// 实例化
JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
return manager;
}
@Bean
PasswordEncoder passwordEncoder() {
// 使用 NoOpPasswordEncoder 作为密码编码器
return NoOpPasswordEncoder.getInstance();
}
}


这里创建了两个用户,user1 和 user2,在 authorities 表中给 user1 添加了授权,没有给 user2 添加授权。user1 可以正常访问,user2 因为没有授权,返回了401。

至此 JdbcUserDetailsManager 就可以工作了,如果数据库表名或列和 JdbcUserDetailsManager 默认的不一致,对象提供了一些方法来修改默认的 SQL 语句,这样就可以灵活发挥了。

Spring Security用户管理详解
1180

被折叠的 条评论
为什么被折叠?



