在实际开发中,使用spring sercurity+redis生成并保存token会出现,不同客户端使用的时同一个token(都是存储在redis中),这将造成,其中一个客户端注销,
其他客户端也被注销了,极大的影响了开发效率,于是通过阅读源码,对token的生成做了修改,实现了每次登陆都是新的token。
参考地址:https://blog.csdn.net/gangsijay888/article/details/81977796
1、DefaultTokenServices
DefaultTokenServices类的createAccessToken方法将会通过当前登陆的用户信息从redis中获取token,若存在,则直接返回,否则生成新的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
View Code
2、RedisTokenStore
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
View Code
3、自定义key的生成规则
通过自定义key的生成规则,实现每次登陆都生成新的token。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 values = new LinkedHashMap();
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(authorizationRequest.getScope())));
20 }
21 return generateKey(values);
22 }
23 }
View Code
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;
}
}
|