在编写好spring security的记住我之后,前台登录产生了remember-me的cookie,然后关闭浏览器再打开请求接口,此时浏览器的remember-me就消失了,在后端debug后发现AbstractRememberMeServices类里的autoLogin方法执行了两遍,第一遍运行没问题,然后第二遍走到processAutoLoginCookie方法后发现presentedToken.equals(token.getTokenValue()的值为false,也就是从tokenRepository里取出的token与接收到的解码后的cookie不一致,然后导致抛出CookieTheftException异常,之后下面的代码就会把数据库存的token和浏览器的cookie清除掉
更新一波
又重新跑了遍debug,发现在processAutoLoginCookie这个方法里,验证presentedToken.equals(token.getTokenValue()为true之后,还走了这一步
tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());
也就是更新了token,所以在第二次验证presentedToken.equals(token.getTokenValue()时,之前的token与更新后的token不一致导致验证失败,下面是processAutoLoginCookie方法的代码
protected UserDetails processAutoLoginCookie(String[] cookieTokens,
HttpServletRequest request, HttpServletResponse response) {
if (cookieTokens.length != 2) {
throw new InvalidCookieException("Cookie token did not contain " + 2
+ " tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
}
final String presentedSeries = cookieTokens[0];
final String presentedToken = cookieTokens[1];
PersistentRememberMeToken token = tokenRepository
.getTokenForSeries(presentedSeries);
if (token == null) {
// No series match, so we can't authenticate using this cookie
throw new RememberMeAuthenticationException(
"No persistent token found for series id: " + presentedSeries);
}
// We have a match for this user/series combination
//这里验证前端传过来的token与数据库存的是否一致,第一次验证是一致的,第二次验证因为下面有个更新token的操作导致验证失败
if (!presentedToken.equals(token.getTokenValue())) {
// Token doesn't match series value. Delete all logins for this user and throw
// an exception to warn them.
tokenRepository.removeUserTokens(token.getUsername());
throw new CookieTheftException(
messages.getMessage(
"PersistentTokenBasedRememberMeServices.cookieStolen",
"Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
}
if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System
.currentTimeMillis()) {
throw new RememberMeAuthenticationException("Remember-me login has expired");
}
// Token also matches, so login is valid. Update the token value, keeping the
// *same* series number.
if (logger.isDebugEnabled()) {
logger.debug("Refreshing persistent login token for user '"
+ token.getUsername() + "', series '" + token.getSeries() + "'");
}
PersistentRememberMeToken newToken = new PersistentRememberMeToken(
token.getUsername(), token.getSeries(), generateTokenData(), new Date());
try {
//在这里token更新了
tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(),
newToken.getDate());
addCookie(newToken, request, response);
}
catch (Exception e) {
logger.error("Failed to update token: ", e);
throw new RememberMeAuthenticationException(
"Autologin failed due to data access problem");
}
return getUserDetailsService().loadUserByUsername(token.getUsername());
}
瞎搞了一下,之前是先记住我登录,然后登录成功后关闭浏览器再打开浏览器直接请求接口,这时remember-me的cookie和数据库里面的token都没了,后面我的步骤跟上面一样,就是关闭浏览器再打开浏览器后,先重启了一下后端再请求接口,结果发现可以正常访问了,可真是神奇