自动登录是将用户的登录信息保存在用户浏览器的cookie中,当用户下次访问时,自动实现校验 并建立登录态的一种机制。
Spring Security提供了两种非常好的令牌:
- 用散列算法加密用户必要的登录信息并生成令牌。
- 数据库等持久性数据存储机制用的持久化令牌。
散列算法在Spring Security中是通过加密几个关键信息实现的:
hashInfo = md5Hex(username + ":" + expirationTime + ":" + password + ":" + key)
rememberCookie = base64(username + ":" + expirationTime + ":" + hashInfo)
其中,expirationTime指本次自动登录的有效期,key为指定的一个散列盐值,用于防止令牌被修 改。通过这种方式生成cookie后,在下次登录时,SpringSecurity首先用Base64简单解码得到用户名、 过期时间和加密散列值;然后使用用户名得到密码;接着重新以该散列算法正向计算,并将计算结果与旧的加密散列值进行对比,从而确认该令牌是否有效。
一、散列加密方案
1、修改配置
1、rememberMeCookieName设置的是生成的cookie的名字
2、rememberMeParameter设置的是前端登录是传参的名字,如下图,SpringSecurity会使用这个名字取前端的参数
3、key是一个散列盐值,没有指定的时候是一个随机的UUID字符串,这样每次启动服务,或者部署多台机器都会导致自动登录使用的cookie失效。所以直接指定一个固定的。
4、userDetailService需要指定,用来查询用户信息进行对比
2.前端代码修改
<div class="layui-form-item">
<div class="layui-input-inline">
<input type="checkbox" name="remember" title="记住我" >
</div>
<div class="layui-form-mid" style="float: right">
<a href="#" class="wait" style="color: #777777">忘记密码</a>
</div>
</div>
具体可以查看开头的demo源码
3.启动服务
登录后,查看cookie,会发现有了一个uc-token。
之前每次重启项目都需要重新登录,现在重新启动服务,发现不需要登录了。
二、持久化令牌方案
持久化令牌方案在交互上与散列加密方案一致,都是在用户勾选Remember-me之后,将生成的令 牌发送到用户浏览器,并在用户下次访问系统时读取该令牌进行认证。不同的是,它采用了更加严谨 的安全性设计。
在持久化令牌方案中,最核心的是series和token两个值,它们都是用MD5散列过的随机字符串。 不同的是,series仅在用户使用密码重新登录时更新,而token会在每一个新的session中都重新生成。
这样设计有什么好处呢?
首先,解决了散列加密方案中一个令牌可以同时在多端登录的问题。持久化方案每个会话都会引发token的更 新,即每个token仅支持单实例登录。
其次,自动登录不会导致series变更,而每次自动登录都需要同时验证series和token两个值,当该 令牌还未使用过自动登录就被盗取时,系统会在非法用户验证通过后刷新 token 值,此时在合法用户的浏览器中,该token值已经失效。当合法用户使用自动登录时,由于该series对应的 token 不同,系统 可以推断该令牌可能已被盗用,从而做一些处理。例如,清理该用户的所有自动登录令牌,并通知该用户可能已被盗号等。
1.数据库表
SpringSecurity相关的代码逻辑已经实现了,对应的实体:
public class PersistentRememberMeToken {
private final String username;
private final String series;
private final String tokenValue;
private final Date date;
public PersistentRememberMeToken(String username, String series, String tokenValue, Date date) {
this.username = username;
this.series = series;
this.tokenValue = tokenValue;
this.date = date;
}
public String getUsername() {
return this.username;
}
public String getSeries() {
return this.series;
}
public String getTokenValue() {
return this.tokenValue;
}
public Date getDate() {
return this.date;
}
}
因此我们需要按照实体,创建一个数据库表:
数据库sql也有提供:
public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {
/** Default SQL for creating the database table to store the tokens */
public static final String CREATE_TABLE_SQL = "create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, "
+ "token varchar(64) not null, last_used timestamp not null)";
这里做了一些小改动,也可以按原样:
-- ---------------
-- 自动登录
-- ---------------
CREATE TABLE `persistent_logins` (
`series` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '主键',
`username` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '用户名',
`token` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'token',
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后使用时间',
PRIMARY KEY (`series`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT ='用户表';
表的主键是series,Spring Security可以通过series查询表中的其他信息。有了存储自动登录信息的表 之后,就可以继续配置Spring Security的Remember-me功能了。由于需要使用持久化令牌方案,所以指定tokenRepository。
2.修改配置类
仍然直接使用SpringSecurity提供的。
3.运行
输入账号密码登录成功后,查看数据库,会有一条数据产生。
重启项目等都不需要再次登录。