在實際開發中,使用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; } }
