在项目中使用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 }