小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
写在前面
今天继续讲Spring Security,前几天我们讲了Spring Security是一种什么样的安全框架以及如何构建用户认证体系
在我看来,用户认证体系分为两部分:一个是用户、一个是认证(我是这样的理解的)。
那么本文先讲其中的一部分:用户对象
Spring Security 中的用户对象
Spring Security 中的用户对象用来描述用户并完成对用户信息的管理,涉及UserDetails、GrantedAuthority、UserDetailsService 和 UserDetailsManager这四个核心对象。
-
UserDetails:指的是 Spring Security 中的用户
-
GrantedAuthority:定义用户的操作权限。
-
UserDetailsService:定义了对 UserDetails 的查询操作。
-
UserDetailsManager:扩展 UserDetailsService,添加了创建用户、修改用户密码等功
看着很简单、很容易理解,接着往下看!
我们先来看承载用户详细信息的 UserDetails 接口:
public interface UserDetails extends Serializable {
//获取该用户的权限信息
Collection<? extends GrantedAuthority> getAuthorities();
//获取密码
String getPassword();
//获取用户名
String getUsername();
//判断该账户是否已失效
boolean isAccountNonExpired();
//判断该账户是否已被锁定
boolean isAccountNonLocked();
//判断该账户的凭证信息是否已失效
boolean isCredentialsNonExpired();
//判断该用户是否可用
boolean isEnabled();
}
通过 UserDetails,我们可以获取用户相关的基础信息,并判断其当前状态。同时,我们也可以看到 UserDetails 中保存着一组 GrantedAuthority 对象。而 GrantedAuthority 指定了一个方法用来获取权限信息,如下所示:
public interface GrantedAuthority extends Serializable {
//获取权限信息
String getAuthority();
}
UserDetails 还存在一个子接口 MutableUserDetails。我们看下
//MutableUserDetails 从名字上来看是不可变的接口
//而可变的内容就是密码
interface MutableUserDetails extends UserDetails {
//设置密码
void setPassword(String password);
}
如何创建用户对象
如果我们想要在应用程序中创建一个 UserDetails 对象,可以使用如下所示的链式语法:
UserDetails user = User.withUsername("admin")
.password("123456")
.authorities("read", "write")
.accountExpired(false)
.disabled(true)
.build();
Spring Security 还专门提供了一个 UserBuilder 对象来辅助构建 UserDetails,使用方式也类似:
User.UserBuilder builder =
User.withUsername("admin");
UserDetails user = builder
.password("123456")
.authorities("read", "write")
.accountExpired(false)
.disabled(true)
.build();
在 Spring Security 中,针对 UserDetails 专门提供了一个 UserDetailsService,该接口用来管理 UserDetails,定义如下:
public interface UserDetailsService {
//根据用户名获取用户信息
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
而 UserDetailsManager 继承了 UserDetailsService,并提供了一批针对 UserDetails 的操作接口:
public interface UserDetailsManager extends UserDetailsService {
//创建用户
void createUser(UserDetails user);
//更新用户
void updateUser(UserDetails user);
//删除用户
void deleteUser(String username);
//修改密码
void changePassword(String oldPassword, String newPassword);
//判断指定用户名的用户是否存在
boolean userExists(String username);
}
如何存储用户
通过上面的讲解,几个核心用户对象之间的关联关系就很清楚了,接下来我们需要进一步明确具体的实现过程。
我们来看 UserDetailsManager 的两个实现类:
- 一个是基于内存存储的 InMemoryUserDetailsManager,
- 一个是基于关系型数据库存储的 JdbcUserDetailsManager。
这里,我们以 JdbcMemoryUserDetailsManager 为例展开分析,它的 createUser 方法如下所示:
public void createUser(final UserDetails user) {
validateUserDetails(user);
getJdbcTemplate().update(createUserSql, ps -> {
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setBoolean(3, user.isEnabled());
int paramCount = ps.getParameterMetaData().getParameterCount();
if (paramCount > 3) {
ps.setBoolean(4, !user.isAccountNonLocked());
ps.setBoolean(5, !user.isAccountNonExpired());
ps.setBoolean(6, !user.isCredentialsNonExpired());
}
});
if (getEnableAuthorities()) {
insertUserAuthorities(user);
}
}
可以看到,这里直接使用了 Spring 框架中的 JdbcTemplate 模板工具类实现了数据的插入,同时完成了 GrantedAuthority 的存储。
UserDetailsManager 是一条相对独立的代码线,为了完成用户信息的配置,还存在另一条代码支线,即 UserDetailsManagerConfigurer。该类维护了一个 UserDetails 列表,并提供了一组 withUser 方法完成用户信息的初始化,如下所示:
private final List<UserDetails> users = new ArrayList<>();
public final C withUser(UserDetails userDetails) {
this.users.add(userDetails);
return (C) this;
}
而 withUser 方法返回的是一个 UserDetailsBuilder 对象,该对象内部使用了前面介绍的 UserBuilder 对象。
因此可以实现类似.withUser("spring_user").password("password1").roles("USER") 这样的链式语法,完成用户信息的设置
总结
今天我们剖析了用户对象机制,将几个类之间的关系也梳理了一番。下面大家可以进去源码再跟一遍。 今天就讲到这里,下一期我们聊聊认证体系中的认证对象,有了用户的处理类,肯定也要聊是如何认证的。 加油,一起学习!!!
弦外之音
感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。也欢迎有问题我们下面评论交流
加油! 我们下期再见!
给大家分享几个我前面写的几篇骚操作