AbstractRememberMeServices有两个实现类
实现类 | 服务端存储token | 校验后更新token有效期 | cookie内容 |
---|---|---|---|
TokenBasedRememberMeServices | 否 | 否 | username, expireTime,token |
PersistentRememberMeToken | 是 | 是 | series,tokenValue |
TokenBasedRememberMeServices
cookie经Base64解码后,获得字符串数组
image.png
内容分别为username, expireTime,token
这个token是按username:tokenExpiryTime:password:key拼接后进行MD5摘要得到
其中key是RememberMeConfigurer初始化通过UUID随机生成
/**
* Calculates the digital signature to be put in the cookie. Default value is MD5
* ("username:tokenExpiryTime:password:key")
*/
protected String makeTokenSignature(long tokenExpiryTime, String username,
String password) {
String data = username + ":" + tokenExpiryTime + ":" + password + ":" + getKey();
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
}
catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
}
return new String(Hex.encode(digest.digest(data.getBytes())));
}
token校验关键点:会再次使用username:tokenExpiryTime:password:key生成token,并与传入的token作比对
此处的password来自userDetails,确保了cookie内容的安全性,并可知若用户更改了密码,该token亦会失效
UserDetails userDetails = getUserDetailsService().loadUserByUsername(
cookieTokens[0]);
Assert.notNull(userDetails, () -> "UserDetailsService " + getUserDetailsService()
+ " returned null for username " + cookieTokens[0] + ". "
+ "This is an interface contract violation");
// Check signature of token matches remaining details.
// Must do this after user lookup, as we need the DAO-derived password.
// If efficiency was a major issue, just add in a UserCache implementation,
// but recall that this method is usually only called once per HttpSession - if
// the token is valid,
// it will cause SecurityContextHolder population, whilst if invalid, will cause
// the cookie to be cancelled.
String expectedTokenSignature = makeTokenSignature(tokenExpiryTime,
userDetails.getUsername(), userDetails.getPassword());
if (!equals(expectedTokenSignature, cookieTokens[2])) {
throw new InvalidCookieException("Cookie token[2] contained signature '"
+ cookieTokens[2] + "' but expected '" + expectedTokenSignature + "'");
}
PersistentTokenBasedRememberMeServices
cookie经Base64解码后,获得字符串数组
image.png
内容分别为series,tokenValue
series,tokenValue均为随机生成的Base64字符串,相当于随机生成一份账密
protected String generateSeriesData() {
byte[] newSeries = new byte[seriesLength];
random.nextBytes(newSeries);
return new String(Base64.getEncoder().encode(newSeries));
}
protected String generateTokenData() {
byte[] newToken = new byte[tokenLength];
random.nextBytes(newToken);
return new String(Base64.getEncoder().encode(newToken));
}
其中series作为key在首次生成后即稳定不变,tokenValue在每次校验后会更新,并与series一同写回cookie
PersistentRememberMeToken newToken = new PersistentRememberMeToken(
token.getUsername(), token.getSeries(), generateTokenData(), new Date());
try {
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");
}
series作为key,将整个PersistentRememberMeToken存储在tokenRepository
PersistentRememberMeToken
包含username,series,tokenValue,date
其中date含义为创建时间,用于判断有效期
自动登录时会刷新tokenValue与date
网友评论