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