博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

spring security oauth2实现多端登录互不影响

Posted on 2021-03-26 13:58  冲杀  阅读(2744)  评论(0编辑  收藏  举报

在项目中使用spring security oauth2做了统一登录授权,在实际开发过程中,发现不同终端同一账号登录,返回的token是一样的。我们使用的是redis存储token,于是查了资料,发现是因为生成token key的算法的原因,导致了多端登录返回一个token的问题,原因如图:

调用代码:

 

 

生成key使用的是DefaultAuthenticationKeyGenerator,代码:

 1 public class DefaultAuthenticationKeyGenerator implements AuthenticationKeyGenerator {
 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     public String extractKey(OAuth2Authentication authentication) {
10         Map<String, String> values = new LinkedHashMap<String, String>();
11         OAuth2Request authorizationRequest = authentication.getOAuth2Request();
12         if (!authentication.isClientOnly()) {
13             values.put(USERNAME, authentication.getName());
14         }
15         values.put(CLIENT_ID, authorizationRequest.getClientId());
16         if (authorizationRequest.getScope() != null) {
17             values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
18         }
19         return generateKey(values);
20     }
21 
22     protected String generateKey(Map<String, String> values) {
23         MessageDigest digest;
24         try {
25             digest = MessageDigest.getInstance("MD5");
26             byte[] bytes = digest.digest(values.toString().getBytes("UTF-8"));
27             return String.format("%032x", new BigInteger(1, bytes));
28         } catch (NoSuchAlgorithmException nsae) {
29             throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).", nsae);
30         } catch (UnsupportedEncodingException uee) {
31             throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).", uee);
32         }
33     }
34 }

从代码里面看,生成key使用的是 client_id、scope、username三个字段,由于这三个字段同一用户在同一子系统中是不变的,所以导致多端登录时,生成的token key是一样的,就会造成返回的token一样,这样的后果就是,其中一个终端退出登录,所有已登录设备就失效了,于是就重写这extractKey方法,继承这个类,增加了一个device_id字段,从而解决多端登录需要互不干扰的需求:

 1 public class CustomAuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {
 2     private static final String CLIENT_ID = "client_id";
 3 
 4     private static final String SCOPE = "scope";
 5 
 6     private static final String USERNAME = "username";
 7 
 8     private static final String DEVICE_ID = "device_id";
 9 
10     @Override
11     public String extractKey(OAuth2Authentication authentication) {
12         Map<String, String> values = new LinkedHashMap<String, String>();
13         OAuth2Request authorizationRequest = authentication.getOAuth2Request();
14         if (!authentication.isClientOnly()) {
15             values.put(USERNAME, authentication.getName());
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 
22         String deviceId = authorizationRequest.getRequestParameters().get(DEVICE_ID);
23         values.put(DEVICE_ID, deviceId);
24 
25         return generateKey(values);
26     }
27 }