1.文档内容
本文档针对普通令牌并用Redis存储的这种方式的底层源码的生成解析方式,并改造底层代码适应现有需求
2.使用Redis存储默认的特征存在的问题
问题:同一个用户多次登录,会返回同一个access_token,这样无法实现一个登录顶替的功能
解决思路:让用户二次登录后重新生成access_token,并让前一次登录生成的access_token立即失效。
3.阅读Access_Token生成源码
①控制access_token逻辑生产的源码在DefaultTokenServices类中
②主要方法是以下的代码:
```java @Transactional public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); OAuth2RefreshToken refreshToken = null; if (existingAccessToken != null) { if (existingAccessToken.isExpired()) { if (existingAccessToken.getRefreshToken() != null) { refreshToken = existingAccessToken.getRefreshToken(); // The token store could remove the refresh token when the // access token is removed, but we want to // be sure… tokenStore.removeRefreshToken(refreshToken); } tokenStore.removeAccessToken(existingAccessToken); } else { // Re-store the access token in case the authentication has changed tokenStore.storeAccessToken(existingAccessToken, authentication); return existingAccessToken; } }
// Only create a new refresh token if there wasn't an existing one
// associated with an expired access token.
// Clients might be holding existing refresh tokens, so we re-use it in
// the case that the old access token
// expired.
if (refreshToken == null) {
refreshToken = createRefreshToken(authentication);
}
// But the refresh token itself might need to be re-issued if it has
// expired.
else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
refreshToken = createRefreshToken(authentication);
}
}
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
// In case it was modified
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}
③代码解释<br />a.如果redis有此用户登录时生成的access_token,并且没有过期,就将上一次用户登录生成的access_token<br />b.如果没有此用户登录的access_token则生成新的access_token<br />④解决方式<br />重新创建一个类,模拟DefaultTokenServices,编写自己的TokenServices,并让SpringSecurityOauth2使用我自己的这套生成access_token的逻辑,其他方法不做修改即可。<br />具体逻辑:<br />①当用户生成token,我会从redis中查询是否有未过期的用户登录access_token<br />②如果有,清除之前的用户登录access_token<br />③生成新的access_token
```java
@Override
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
//清除原始accessToken和refreshToken
if (existingAccessToken != null) {
if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
}
tokenStore.removeAccessToken(existingAccessToken);
}
refreshToken = createRefreshToken(authentication);
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
// In case it was modified
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}
4.修改后的特征
1.同一用户重复登录会生成新的access_token和refresh_token
2.同一个用户只有最新的access_token、refresh_token有效,其他的都会失效。