序言
实现“记住我” 功能 ,在用户登陆一次以后,系统会记住用户一段时间,在这段时间,用户不用反复登陆就可以使用我们的系统。
代码请参考 https://github.com/AutismSuperman/springsecurity-example
先说一下使用
改动
拿之前第的入门程序举例子
改一些login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h3>表单登录</h3>
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>记住我:</td>
<td><input type="checkbox" name="remember-me"></td>
</tr>
<tr>
<td colspan="2">
<button type="button" onclick="login()">登录</button>
</td>
</tr>
</table>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
function login() {
var username = $("input[name=username]").val();
var password = $("input[name=password]").val();
var rememberMe = $("input[name=remember-me]").val();
if (username === "" || password === "") {
alert("用户名或密码不能为空");
return;
}
$.ajax({
type: "POST",
url: "/authentication/form",
data: {
"username": username,
"password": password,
"remember-me": rememberMe,
},
success: function (e) {
console.log(e);
alert("登陆成功")
setTimeout(function () {
location.href = '/hello';
}, 500);
},
error: function (e,a,b) {
console.log(a);
console.log(b);
console.log(e.responseText);
alert("登陆失败zxczxczc")
}
});
}
</script>
</body>
</html>
在登陆页添加自动登录的选项,注意自动登录字段的 name 必须是 remember-me
两种方式
cookie
没的说 ,在WebSecurityConfigurerAdapter
中 的 configure()
方法添加一个 ``rememberMe() 就行了
@Override
protected void configure(HttpSecurity http) throws Exception {
//http.httpBasic() //httpBasic 登录
http.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authentication/form") // 自定义登录路径
.failureHandler(failureAuthenticationHandler) // 自定义登录失败处理
.successHandler(successAuthenticationHandler) // 自定义登录成功处理
.and()
.logout()
.logoutUrl("/logout")
.and()
.authorizeRequests()// 对请求授权
.antMatchers("/login", "/authentication/require",
"/authentication/form").permitAll()
.anyRequest() // 任何请求
.authenticated()//; // 都需要身份认证
.and()
.rememberMe()
.rememberMeCookieName("remember")
.tokenValiditySeconds(3600)
.and()
.csrf().disable();// 禁用跨站攻击
}
然后在登陆的时候勾选一下,这时候登陆成功后,会在浏览器cookie里自动保存一条名为remember-me
的cookie如果你需要改变这个名字可以在 HttpSecurity
链上加入.rememberMeCookieName("remember")
即可。
数据库存储
使用 Cookie 存储虽然很方便,但是大家都知道 Cookie 毕竟是保存在客户端的,而且 Cookie 的值还与用户名、密码这些敏感数据相关,虽然加密了,但是将敏感信息存在客户端,毕竟不太安全。
Spring security 还提供了另一种相对更安全的实现机制:在客户端的 Cookie 中,仅保存一个无意义的加密串(与用户名、密码等敏感数据无关),然后在数据库中保存该加密串-用户信息的对应关系,自动登录时,用 Cookie 中的加密串,到数据库中验证,如果通过,自动登录才算通过。
代码实现
首先需要创建一张表来存储 token 信息:
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后创建一个配置类
@Configuration
public class RemberMeConfig {
private final DataSource dataSource;
public RemberMeConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
// 如果token表不存在,使用下面语句可以初始化该表;若存在,请注释掉这条语句,否则会报错。
//tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
}
然后在 configure()
中稍微做修改
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
//http.httpBasic() //httpBasic 登录
http.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authentication/form") // 自定义登录路径
.failureHandler(failureAuthenticationHandler) // 自定义登录失败处理
.successHandler(successAuthenticationHandler) // 自定义登录成功处理
.and()
.logout()
.logoutUrl("/logout")
.and()
.authorizeRequests()// 对请求授权
.antMatchers("/login", "/authentication/require",
"/authentication/form").permitAll()
.anyRequest() // 任何请求
.authenticated()//; // 都需要身份认证
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(3600)
.userDetailsService(userDetailsService)
.and()
.csrf().disable();// 禁用跨站攻击
}
然后运行程序 勾选自动登录后,Cookie 和数据库中均存储了 token 信息。
基本原理
以前我们说过整个认证的流程,这个流程大概是这样的
UsernamePasswordAuthenticationFilter
认证成功后会走successfulAuthentication
方法- 然后经过
successfulAuthentication
的RememberMeService
其中有个TokenRepository
TokenRepository
生成token,首先将 token 写入到浏览器的 Cookie 中,然后将 token、认证成功的用户名写入到数据库中- 下次请求时,会经过
RememberMeAuthenticationFilter
它会读取 Cookie 中的 token,交给RememberMeService
从数据库中查询记录 - 如果存在记录,会读取用户名并去调用
UserDetailsService
,获取用户信息,并将用户信息放入Spring Security 中,实现自动登陆。
注意的是,过滤连在怎么玩都是链式的 由此我们可以知道
RememberMeAuthenticationFilter
在整个过滤器链中是比较靠后的位置,也就是说在传统登录方式都无法登录的情况下才会使用自动登陆。
本博文是基于springboot2.x 和security 5 如果有什么不对的请在下方留言。