记住我基本原理
- 用户认证成功之后调用
RemeberMeService根据用户名名生成Token由TokenRepository写入到数据库,同时也将Token写入到浏览器的Cookie中 - 重启服务之后,用户再次登入系统会由
RememberMeAuthenticationFilter拦截,从Cookie中读取Token信息,与persistent_logins表匹配判断是否使用记住我功能。最中由UserDetailsService查询用户信息
记住我实现
- 创建persistent_logins表
create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null); 复制代码
- 登陆页面添加记住我复选款(name必须是remeber-me)
<input name="remember-me" type="checkbox"> 下次自动登录 复制代码
- 配置MerryyouSecurityConfig
http. ...... .and() .rememberMe() .tokenRepository(persistentTokenRepository())//设置操作表的Repository .tokenValiditySeconds(securityProperties.getRememberMeSeconds())//设置记住我的时间 .userDetailsService(userDetailsService)//设置userDetailsService .and() ...... 复制代码
效果如下
源码分析
首次登录
AbstractAuthenticationProcessingFilter#successfulAuthentication
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
//# 1.将已认证过的Authentication放入到SecurityContext中
SecurityContextHolder.getContext().setAuthentication(authResult);
//# 2.登录成功调用rememberMeServices
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
复制代码
- 将已认证过的Authentication放入到SecurityContext中
- 登录成功调用rememberMeServices
AbstractRememberMeServices#loginSuccess
private String parameter = DEFAULT_PARAMETER;//remember-me
public final void loginSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication successfulAuthentication) {
// #1.判断是否勾选记住我
if (!rememberMeRequested(request, parameter)) {
logger.debug("Remember-me login not requested.");
return;
}
onLoginSuccess(request, response, successfulAuthentication);
}
复制代码
- 判断是否勾选记住我
PersistentTokenBasedRememberMeServices#onLoginSuccess
protected void onLoginSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication successfulAuthentication) {
//#1.获取用户名
String username = successfulAuthentication.getName();
logger.debug("Creating new persistent login for user " + username);
//#2.创建Token
PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
username, generateSeriesData(), generateTokenData(), new Date());
try {
//#3.存储都数据库
tokenRepository.createNewToken(persistentToken);
//#4.写入到浏览器的Cookie中
addCookie(persistentToken, request, response);
}
catch (Exception e) {
logger.error("Failed to save persistent token ", e);
}
}
复制代码
- 获取用户名
- 创建Token
- 存储都数据库
- 写入到浏览器的Cookie中
二次登录Remember-me
RememberMeAuthenticationFilter#doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//#1.判断SecurityContext中没有Authentication
if (SecurityContextHolder.getContext().getAuthentication() == null) {
//#2.从Cookie查询用户信息返回RememberMeAuthenticationToken
Authentication rememberMeAuth = rememberMeServices.autoLogin(request,
response);
if (rememberMeAuth != null) {
// Attempt authenticaton via AuthenticationManager
try {
//#3.如果不为空则由authenticationManager认证
rememberMeAuth = authenticationManager.authenticate(rememberMeAuth);
// Store to SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
onSuccessfulAuthentication(request, response, rememberMeAuth);
......
复制代码
- 判断SecurityContext中没有Authentication
- 从Cookie查询用户信息返回RememberMeAuthenticationToken
- 如果不为空则由authenticationManager认证
AbstractRememberMeServices#autoLogin
public final Authentication autoLogin(HttpServletRequest request,
HttpServletResponse response) {
//#1.获取Cookie
String rememberMeCookie = extractRememberMeCookie(request);
if (rememberMeCookie == null) {
return null;
}
logger.debug("Remember-me cookie detected");
if (rememberMeCookie.length() == 0) {
logger.debug("Cookie was empty");
cancelCookie(request, response);
return null;
}
UserDetails user = null;
try {
//#2.解析Cookie
String[] cookieTokens = decodeCookie(rememberMeCookie);
//#3.获取用户凭证
user = processAutoLoginCookie(cookieTokens, request, response);
//#4.检查用户凭证
userDetailsChecker.check(user);
logger.debug("Remember-me cookie accepted");
//#5.返回Authentication
return createSuccessfulAuthentication(request, user);
}
catch (CookieTheftException cte) {
cancelCookie(request, response);
throw cte;
}
catch (UsernameNotFoundException noUser) {
logger.debug("Remember-me login was valid but corresponding user not found.",
noUser);
}
catch (InvalidCookieException invalidCookie) {
logger.debug("Invalid remember-me cookie: " + invalidCookie.getMessage());
}
catch (AccountStatusException statusInvalid) {
logger.debug("Invalid UserDetails: " + statusInvalid.getMessage());
}
catch (RememberMeAuthenticationException e) {
logger.debug(e.getMessage());
}
cancelCookie(request, response);
return null;
}
复制代码
- 获取Cookie
- 解析Cookie
- 获取用户凭证
- 检查用户凭证
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 记住变量类型的三种方式
- Android-保你能记住的生命周期教程
- springboot + shiro 验证码与记住登录
- 谷歌最新举动:安卓用户无需再记住密码
- Angular APP开发时要记住的事项
- 您真的记住了吗?游戏知识速记卡・第二期
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Web Designer's Idea Book, Vol. 2
Patrick McNeil / How / 2010-9-19 / USD 30.00
Web Design Inspiration at a Glance Volume 2 of The Web Designer's Idea Book includes more than 650 new websites arranged thematically, so you can easily find inspiration for your work. Auth......一起来看看 《The Web Designer's Idea Book, Vol. 2》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
UNIX 时间戳转换
UNIX 时间戳转换