spring sercurity+redis实现每次登陆生成不同的token
在实际开发中,使用spring sercurity+redis生成并保存token会出现,不同客户端使用的时同一个token(都是存储在redis中),这将造成,其中一个客户端注销,
其他客户端也被注销了,极大的影响了开发效率,于是通过阅读源码,对token的生成做了修改,实现了每次登陆都是新的token。
参考地址:https://blog.csdn.net/gangsijay888/article/details/81977796
1、DefaultTokenServices
DefaultTokenServices类的createAccessToken方法将会通过当前登陆的用户信息从redis中获取token,若存在,则直接返回,否则生成新的。
1 @Transactional 2 public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { 3 //从redis中获取登陆信息 4 OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); 5 OAuth2RefreshToken refreshToken = null; 6 if (existingAccessToken != null) { 7 if (existingAccessToken.isExpired()) { 8 if (existingAccessToken.getRefreshToken() != null) { 9 refreshToken = existingAccessToken.getRefreshToken(); 10 // The token store could remove the refresh token when the 11 // access token is removed, but we want to 12 // be sure... 13 tokenStore.removeRefreshToken(refreshToken); 14 } 15 tokenStore.removeAccessToken(existingAccessToken); 16 } 17 else { 18 // Re-store the access token in case the authentication has changed 19 tokenStore.storeAccessToken(existingAccessToken, authentication); 20 return existingAccessToken; 21 } 22 } 23 24 // Only create a new refresh token if there wasn't an existing one 25 // associated with an expired access token. 26 // Clients might be holding existing refresh tokens, so we re-use it in 27 // the case that the old access token 28 // expired. 29 if (refreshToken == null) { 30 refreshToken = createRefreshToken(authentication); 31 } 32 // But the refresh token itself might need to be re-issued if it has 33 // expired. 34 else if (refreshToken instanceof ExpiringOAuth2RefreshToken) { 35 ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; 36 if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { 37 refreshToken = createRefreshToken(authentication); 38 } 39 } 40 41 OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); 42 tokenStore.storeAccessToken(accessToken, authentication); 43 // In case it was modified 44 refreshToken = accessToken.getRefreshToken(); 45 if (refreshToken != null) { 46 tokenStore.storeRefreshToken(refreshToken, authentication); 47 } 48 return accessToken; 49 50 }
2、RedisTokenStore
1 @Override 2 public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { 3 //根据登陆时的用户信息并按照一定规则生成key,然后通过key从Reid是中获取 4 //数据 5 String key = authenticationKeyGenerator.extractKey(authentication); 6 byte[] serializedKey = serializeKey(AUTH_TO_ACCESS + key); 7 byte[] bytes = null; 8 RedisConnection conn = getConnection(); 9 try { 10 bytes = conn.get(serializedKey); 11 } finally { 12 conn.close(); 13 } 14 OAuth2AccessToken accessToken = deserializeAccessToken(bytes); 15 if (accessToken != null) { 16 OAuth2Authentication storedAuthentication = readAuthentication(accessToken.getValue()); 17 if ((storedAuthentication == null || !key.equals(authenticationKeyGenerator.extractKey(storedAuthentication)))) { 18 // Keep the stores consistent (maybe the same user is 19 // represented by this authentication but the details have 20 // changed) 21 storeAccessToken(accessToken, authentication); 22 } 23 24 } 25 return accessToken; 26 }
3、自定义key的生成规则
通过自定义key的生成规则,实现每次登陆都生成新的token。
1 public class MyAuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator { 2 3 private static final String CLIENT_ID = "client_id"; 4 5 private static final String SCOPE = "scope"; 6 7 private static final String USERNAME = "username"; 8 9 @Override 10 public String extractKey(OAuth2Authentication authentication) { 11 Map<String, String> values = new LinkedHashMap<String, String>(); 12 OAuth2Request authorizationRequest = authentication.getOAuth2Request(); 13 if (!authentication.isClientOnly()) { 14 //在用户名后面添加时间戳,使每次的key都不一样 15 values.put(USERNAME, authentication.getName()+System.currentTimeMillis()); 16 } 17 values.put(CLIENT_ID, authorizationRequest.getClientId()); 18 if (authorizationRequest.getScope() != null) { 19 values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope()))); 20 } 21 return generateKey(values); 22 } 23 }
4、将自定义的key生成规则注入到RedisTokenStore中
@Configuration
public class MyTokenStoreConfig {
//同一个账户是否每次登陆使用同一个token
@Value("${token.oneAccount.onlyOne}")
private boolean onlyOne;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore tokenStore() {
RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
if (!onlyOne){
redisTokenStore.setAuthenticationKeyGenerator(new MyAuthenticationKeyGenerator());
}
return redisTokenStore;
}
}