上一篇:https://lawsssscat.blog.csdn.net/article/details/105321389
三部分
- 记住我功能基本原理
- 记住我功能具体实现
- 记住我功能Spring Security源码解析
记住我功能基本原理
-
当用户登录时,发起认证请求给
UsernamePasswordAuthenticationFilter
-
当认证成功,会调用
RememberMeService
服务
-
RememberMeService
服务会生成一个Token
首先,会把Token
写入到浏览器的 Cookie 里面。
然后,RememberMeService
里面有 TokenRepository
RememberMeService
会使用 TokenRepository 把Token
写入到数据库里面
<mark>另外一个会话</mark>
-
当浏览器携带有
Token
的Cookie
请求服务
会来到RememberMeAuthenticationFilter
-
RememberMeAuthenticationFilter
会读取请求中的Cookie
并取出Token
部分交给RememberMeService
-
RememberMeService
借助TokenRepository
从数据库中查找有没有对应的Token
记录
如果有对应的Token
记录,就返回对应的用户信息(UserDetails
),并交给UserDetailsService
-
UserDetailsService
就用UserDetails
获取用户信息,并放入SecurityContext
里面,这样就把用户登录上了
RememberMeAuthenticationFilter 的位置,是当用户密码,httpBasic认证都没用时,起作用
记住我功能具体实现
登录界面添加按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>登录22222222222</h2>
<!--路径是我们自定义的,默认值为/login,在UsernamePasswordAuthenticationFilter中定义-->
<form method="post" action="/authentication/form">
username: <input type="text" name="username"><br>
pawssword: <input type="text" name="password"><br>
图形验证码:<input type="text" name="imageCode"> <img src="/code/image?width=600"><br>
<input type="checkbox" value="true" name="remember-me">记住我<br>
<input type="submit" value="登录">
</form>
</body>
</html>
配置 TokenRepository
在 v-security-browser 的配置类里面配置
在 BrowserSecurityConfig 里面添加代码
@Autowired
private UserDetailsService userDetailsService ;
@Autowired
private DataSource dataSource;
/** * @return RememberMe 功能的 Repository */
@Bean
public PersistentTokenRepository persistentTokenRepository() {
// 连接数据库的实现类,还有一个实现类时存内存的InMemoryTokenRepositoryImpl
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
// 配置数据源
tokenRepository.setDataSource(dataSource);
// 在启动数据库时创建存储token的表
tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
配置记住我生效时间
在 BrowserProperties 里面配置
配置数据源信息(很早前在v-security-demo里面就配过了)
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://vdb.cn:3306/vsdb?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
username: root
password: root
配置类中整合前面所有配置
.and()
// 记住我配置
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
.userDetailsService(userDetailsService)
启动系统
看数据库
可以看到多了一张表
这是因为
tokenRepository.setCreateTableOnStartup(true);
这句代码起作用
当里面为 true 时,项目启动,JdbcTokenRepositoryImpl就会执行它内部写死的一段建表代码(下图)
<mark>再次启动时,要注掉下面代码,因为表已存在,无法再创建</mark>
登录
访问:http://localhost:8080/login2.html
<mark>记住记住我</mark>(下图)
访问:http://localhost:8080/user
访问没问题(下图)
这时去看数据库,会在表persistent_logins中多出一条数据
这时去看浏览器的application的cookie里,也能看到 remember-me 的值
删除cookie里的 JSESSIONID,还是可以再次访问 http://localhost:8080/user
这就是下面一个cokkie : remember-me 的作用
(注意 remember-me 中有过期时间,当过期时间没到,关闭浏览器,remember-me会被序列化到硬盘,当再次打开浏览器时被反序列化。因此,可以看作,在过期时间前,会在一直存在于浏览器中,并且伴随请求被发送到服务器)
done~
如果返回提示:“content”: “验证码的值不能为空”
进行下面注释
记住我功能 Spring Security 源码解析
# 登录
<mark>在 UsernamePasswordAuthenticationFilter 的 attemptAuthentication 中设置断点</mark>(下图)
重新登录 http://localhost:8080/login2.html
它是被 AbstractAuthenticationProcessingFilter
调用 (下图)
当一切成功 会进入 successfulAuthentication 方法(下图)
successfulAuthentication 里面有 rememberMeServices 的调用
调用方法 onLoginSuccess
# 使用cookie中的token
在 RememberMeAuthenticationFilter 的 doFilter 中添加断点
删除cookie中的 JSESSIONID,重新访问 http://localhost:8080/user
processAutoLoginCookie 方法就是访问数据库对照 token 获取 UserDetails的
最终用 getUserDetailsService 获取 UserDetails
done~