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     }    
View Code

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     }
View Code

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 }
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;
    }
}

 

 

posted @ 2020-06-29 11:30  炫舞风中  阅读(2252)  评论(0编辑  收藏  举报