【转载】spring-security-oauth2(二十三) 基本令牌配置及JWT扩展解析

令牌前面我们用的都是默认配置,其实令牌的样式及存储我们都可以进行定制,这章我们主要完成

  • 基本的Token参数配置
  • 使用jwt替换默认的token
  • 扩展和解析jwt的信息

基本的Token参数配置

硬编码配置

token是在认证服务器处理的。

springboot2.x和1.x的配置不太一样,这点我们要注意。分析源码可知


 
 
  1. OAuth2AuthorizationServerConfiguration 类是 @EnableAuthorizationServer 的自动配置类; 
  2. 如果我们 继承了 AuthorizationServerConfigurerAdapter,那么该类将不会被初始化,认证服务器将不能正常工作。
  3. 下面我们来进行改造

 
 
  1. package com.rui.tiger.auth.app;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.security.authentication.AuthenticationManager;
  5. import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
  6. import org.springframework.security.core.userdetails.UserDetailsService;
  7. import org.springframework.security.crypto.password.NoOpPasswordEncoder;
  8. import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
  9. import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
  10. import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
  11. import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
  12. import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
  13. /**
  14. * 服务提供商-认证服务器
  15. * @author CaiRui
  16. * @date 2019-03-18 09:12
  17. */
  18. @Configuration
  19. @EnableAuthorizationServer
  20. public class TigerAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
  21. private final AuthenticationManager authenticationManager;
  22. public TigerAuthorizationServerConfig(AuthenticationConfiguration authenticationConfiguration) throws Exception {
  23. this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
  24. }
  25. @Override
  26. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  27. clients.inMemory()
  28. .withClient( "tigerauth")
  29. .secret( "123456")
  30. .redirectUris( "http://example.com", "hlocalhost:80")
  31. .authorizedGrantTypes( "refresh_token", "password")
  32. .accessTokenValiditySeconds( 7200)
  33. .scopes( "all")
  34. .and()
  35. .withClient( "myid2")
  36. .secret( "myid2")
  37. .redirectUris( "http://example.com", "localhost:8080")
  38. .authorizedGrantTypes( "refresh_token", "password")
  39. .accessTokenValiditySeconds( 7200)
  40. .scopes( "all", "read", "write");
  41. }
  42. @Override
  43. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  44. endpoints.authenticationManager( this.authenticationManager);
  45. }
  46. @Override
  47. public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
  48. // 这里使用什么密码需要 根据上面配置client信息里面的密码类型决定
  49. // 目前上面配置的是无加密的密码
  50. security.passwordEncoder(NoOpPasswordEncoder.getInstance());
  51. }
  52. }

 
 
  1. 把我们的配置文件中的app信息可以注释了
  2. 我们来用密码模式来测试下

可以看到token过期时间已经生效,app信息硬编码不够灵活,下面我们来通过配置文件来进行配置

配置文件配置

首先定义配置关系实体类


 
 
  1. package com.rui.tiger.auth.core.properties;
  2. /**
  3. * oauth2客户端配置
  4. * @author CaiRui
  5. * @Date 2019-05-01 16:17
  6. */
  7. public class OAuth2ClientProperties {
  8. private String clientId;
  9. private String clientSecret;
  10. private String[] authorizedGrantTypes = {}; //授权类型
  11. private String[] redirectUris = {}; // 信任的回调域
  12. private String[] scopes = {};
  13. private int accessTokenValiditySeconds; // token有效期 默认单位秒
  14. public String getClientId() {
  15. return clientId;
  16. }
  17. public void setClientId(String clientId) {
  18. this.clientId = clientId;
  19. }
  20. public String getClientSecret() {
  21. return clientSecret;
  22. }
  23. public void setClientSecret(String clientSecret) {
  24. this.clientSecret = clientSecret;
  25. }
  26. public String[] getAuthorizedGrantTypes() {
  27. return authorizedGrantTypes;
  28. }
  29. public void setAuthorizedGrantTypes(String[] authorizedGrantTypes) {
  30. this.authorizedGrantTypes = authorizedGrantTypes;
  31. }
  32. public String[] getRedirectUris() {
  33. return redirectUris;
  34. }
  35. public void setRedirectUris(String[] redirectUris) {
  36. this.redirectUris = redirectUris;
  37. }
  38. public String[] getScopes() {
  39. return scopes;
  40. }
  41. public void setScopes(String[] scopes) {
  42. this.scopes = scopes;
  43. }
  44. public int getAccessTokenValiditySeconds() {
  45. return accessTokenValiditySeconds;
  46. }
  47. public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
  48. this.accessTokenValiditySeconds = accessTokenValiditySeconds;
  49. }
  50. }

 
 
  1. package com.rui.tiger.auth.core.properties;
  2. /**
  3. * oauth2配置
  4. * @author CaiRui
  5. * @Date 2019-05-01 16:17
  6. */
  7. public class OAuth2Properties {
  8. private OAuth2ClientProperties[] clients = {};
  9. public OAuth2ClientProperties[] getClients() {
  10. return clients;
  11. }
  12. public void setClients(OAuth2ClientProperties[] clients) {
  13. this.clients = clients;
  14. }
  15. }

加到权限基本配置SecurityProperties中 

同时修改我们的配置文件,注意yaml格式数组的写法


 
 
  1. oauth2:
  2. clients:
  3. -
  4. clientId: tigerauth
  5. clientSecret: 123456
  6. authorizedGrantTypes: ["refresh_token", "password"]
  7. scopes: ["all", "read"]
  8. redirectUris: ["http://example.com","localhost:80"]
  9. accessTokenValiditySeconds: 7200
  10. -
  11. clientId: myid2
  12. clientSecret: myid2
  13. authorizedGrantTypes: ["refresh_token", "password"]
  14. scopes: ["all", "read", "write"]
  15. redirectUris: ["http://example.com","localhost:80"]
  16. accessTokenValiditySeconds: 7200

修改我么的认证服务器为配置


 
 
  1. package com.rui.tiger.auth.app;
  2. import com.rui.tiger.auth.core.properties.OAuth2ClientProperties;
  3. import com.rui.tiger.auth.core.properties.SecurityProperties;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.security.authentication.AuthenticationManager;
  8. import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
  9. import org.springframework.security.core.userdetails.UserDetailsService;
  10. import org.springframework.security.crypto.password.NoOpPasswordEncoder;
  11. import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
  12. import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
  13. import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
  14. import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
  15. import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
  16. import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
  17. import java.util.Arrays;
  18. /**
  19. * 服务提供商-认证服务器
  20. * @author CaiRui
  21. * @date 2019-03-18 09:12
  22. */
  23. @Configuration
  24. @EnableAuthorizationServer
  25. @Slf4j
  26. public class TigerAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
  27. @Autowired
  28. private SecurityProperties securityProperties;
  29. private final AuthenticationManager authenticationManager;
  30. public TigerAuthorizationServerConfig(AuthenticationConfiguration authenticationConfiguration) throws Exception {
  31. this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
  32. }
  33. @Override
  34. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  35. /* clients.inMemory()
  36. .withClient("tigerauth")
  37. .secret("123456")
  38. .redirectUris("http://example.com", "localhost:80")
  39. .authorizedGrantTypes("refresh_token", "password")
  40. .accessTokenValiditySeconds(7200)
  41. .scopes("all")
  42. .and()
  43. .withClient("myid2")
  44. .secret("myid2")
  45. .redirectUris("http://example.com", "localhost:8080")
  46. .authorizedGrantTypes("refresh_token", "password")
  47. .accessTokenValiditySeconds(7200)
  48. .scopes("all", "read", "write");*/
  49. //配置文件解析token配置
  50. InMemoryClientDetailsServiceBuilder inMemory = clients.inMemory();
  51. OAuth2ClientProperties[] clientsInCustom = securityProperties.getOauth2().getClients();
  52. for (OAuth2ClientProperties p : clientsInCustom) {
  53. inMemory.withClient(p.getClientId())
  54. .secret(p.getClientSecret())
  55. .redirectUris(p.getRedirectUris())
  56. .authorizedGrantTypes(p.getAuthorizedGrantTypes())
  57. .accessTokenValiditySeconds(p.getAccessTokenValiditySeconds())
  58. .scopes(p.getScopes());
  59. }
  60. }
  61. @Override
  62. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  63. endpoints.authenticationManager( this.authenticationManager);
  64. }
  65. @Override
  66. public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
  67. // 这里使用什么密码需要 根据上面配置client信息里面的密码类型决定
  68. // 目前上面配置的是无加密的密码
  69. security.passwordEncoder(NoOpPasswordEncoder.getInstance());
  70. }
  71. }

测试下

ok 我们换个不在scope中的来试试

redis存储

上面的的配置信息都是基于内存存储令牌信息,我们改用redis来存储

 

配置我们自定义的tokenStore


 
 
  1. package com.rui.tiger.auth.app;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.redis.connection.RedisConnectionFactory;
  6. import org.springframework.security.oauth2.provider.token.TokenStore;
  7. import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
  8. /**
  9. * redis存储token
  10. * @author CaiRui
  11. * @Date 2019-05-01 17:44
  12. */
  13. @Configuration
  14. public class TokenStoreConfig {
  15. @Autowired
  16. private RedisConnectionFactory redisConnectionFactory;
  17. @Bean
  18. public TokenStore redisTokenStore() {
  19. return new MyRedisTokenStore(redisConnectionFactory);
  20. }
  21. }

spring-data-redis 2.0版本中set(String,String)被弃用了,要使用RedisConnection.stringCommands().set(…),所有我自定义一个RedisTokenStore,代码和RedisTokenStore一样,只是把所有conn.set(…)都换成conn..stringCommands().set(…),详情如下

https://blog.csdn.net/smollsnail/article/details/78954225


 
 
  1. package com.rui.tiger.auth.app;
  2. import org.springframework.data.redis.connection.RedisConnection;
  3. import org.springframework.data.redis.connection.RedisConnectionFactory;
  4. import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
  5. import org.springframework.security.oauth2.common.OAuth2AccessToken;
  6. import org.springframework.security.oauth2.common.OAuth2RefreshToken;
  7. import org.springframework.security.oauth2.provider.OAuth2Authentication;
  8. import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
  9. import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
  10. import org.springframework.security.oauth2.provider.token.TokenStore;
  11. import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
  12. import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
  13. import java.util.*;
  14. /**
  15. * org.springframework.data.redis.connection.RedisConnection.set([B[B)V 异常处理
  16. *
  17. * spring-data-redis 2.0版本中set(String,String)被弃用了,要使用RedisConnection.stringCommands().set(…),所有我自定义一个RedisTokenStore,代码和RedisTokenStore一样,只是把所有conn.set(…)都换成conn..stringCommands().set(…),
  18. * 参见: https://blog.csdn.net/smollsnail/article/details/78954225
  19. *
  20. * @author CaiRui
  21. * @Date 2019-05-01 20:36
  22. */
  23. public class MyRedisTokenStore implements TokenStore {
  24. private static final String ACCESS = "access:";
  25. private static final String AUTH_TO_ACCESS = "auth_to_access:";
  26. private static final String AUTH = "auth:";
  27. private static final String REFRESH_AUTH = "refresh_auth:";
  28. private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
  29. private static final String REFRESH = "refresh:";
  30. private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
  31. private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
  32. private static final String UNAME_TO_ACCESS = "uname_to_access:";
  33. private final RedisConnectionFactory connectionFactory;
  34. private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
  35. private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
  36. private String prefix = "";
  37. public MyRedisTokenStore(RedisConnectionFactory connectionFactory) {
  38. this.connectionFactory = connectionFactory;
  39. }
  40. public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
  41. this.authenticationKeyGenerator = authenticationKeyGenerator;
  42. }
  43. public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {
  44. this.serializationStrategy = serializationStrategy;
  45. }
  46. public void setPrefix(String prefix) {
  47. this.prefix = prefix;
  48. }
  49. private RedisConnection getConnection() {
  50. return this.connectionFactory.getConnection();
  51. }
  52. private byte[] serialize(Object object) {
  53. return this.serializationStrategy.serialize(object);
  54. }
  55. private byte[] serializeKey(String object) {
  56. return this.serialize( this.prefix + object);
  57. }
  58. private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
  59. return (OAuth2AccessToken) this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
  60. }
  61. private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
  62. return (OAuth2Authentication) this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
  63. }
  64. private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
  65. return (OAuth2RefreshToken) this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
  66. }
  67. private byte[] serialize(String string) {
  68. return this.serializationStrategy.serialize(string);
  69. }
  70. private String deserializeString(byte[] bytes) {
  71. return this.serializationStrategy.deserializeString(bytes);
  72. }
  73. public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
  74. String key = this.authenticationKeyGenerator.extractKey(authentication);
  75. byte[] serializedKey = this.serializeKey( "auth_to_access:" + key);
  76. byte[] bytes = null;
  77. RedisConnection conn = this.getConnection();
  78. try {
  79. bytes = conn.get(serializedKey);
  80. } finally {
  81. conn.close();
  82. }
  83. OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
  84. if(accessToken != null) {
  85. OAuth2Authentication storedAuthentication = this.readAuthentication(accessToken.getValue());
  86. if(storedAuthentication == null || !key.equals( this.authenticationKeyGenerator.extractKey(storedAuthentication))) {
  87. this.storeAccessToken(accessToken, authentication);
  88. }
  89. }
  90. return accessToken;
  91. }
  92. public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
  93. return this.readAuthentication(token.getValue());
  94. }
  95. public OAuth2Authentication readAuthentication(String token) {
  96. byte[] bytes = null;
  97. RedisConnection conn = this.getConnection();
  98. try {
  99. bytes = conn.get( this.serializeKey( "auth:" + token));
  100. } finally {
  101. conn.close();
  102. }
  103. OAuth2Authentication var4 = this.deserializeAuthentication(bytes);
  104. return var4;
  105. }
  106. public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
  107. return this.readAuthenticationForRefreshToken(token.getValue());
  108. }
  109. public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
  110. RedisConnection conn = this.getConnection();
  111. OAuth2Authentication var5;
  112. try {
  113. byte[] bytes = conn.get( this.serializeKey( "refresh_auth:" + token));
  114. OAuth2Authentication auth = this.deserializeAuthentication(bytes);
  115. var5 = auth;
  116. } finally {
  117. conn.close();
  118. }
  119. return var5;
  120. }
  121. public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
  122. byte[] serializedAccessToken = this.serialize((Object)token);
  123. byte[] serializedAuth = this.serialize((Object)authentication);
  124. byte[] accessKey = this.serializeKey( "access:" + token.getValue());
  125. byte[] authKey = this.serializeKey( "auth:" + token.getValue());
  126. byte[] authToAccessKey = this.serializeKey( "auth_to_access:" + this.authenticationKeyGenerator.extractKey(authentication));
  127. byte[] approvalKey = this.serializeKey( "uname_to_access:" + getApprovalKey(authentication));
  128. byte[] clientId = this.serializeKey( "client_id_to_access:" + authentication.getOAuth2Request().getClientId());
  129. RedisConnection conn = this.getConnection();
  130. try {
  131. conn.openPipeline();
  132. conn.stringCommands().set(accessKey, serializedAccessToken);
  133. conn.stringCommands().set(authKey, serializedAuth);
  134. conn.stringCommands().set(authToAccessKey, serializedAccessToken);
  135. if(!authentication.isClientOnly()) {
  136. conn.rPush(approvalKey, new byte[][]{serializedAccessToken});
  137. }
  138. conn.rPush(clientId, new byte[][]{serializedAccessToken});
  139. if(token.getExpiration() != null) {
  140. int seconds = token.getExpiresIn();
  141. conn.expire(accessKey, ( long)seconds);
  142. conn.expire(authKey, ( long)seconds);
  143. conn.expire(authToAccessKey, ( long)seconds);
  144. conn.expire(clientId, ( long)seconds);
  145. conn.expire(approvalKey, ( long)seconds);
  146. }
  147. OAuth2RefreshToken refreshToken = token.getRefreshToken();
  148. if(refreshToken != null && refreshToken.getValue() != null) {
  149. byte[] refresh = this.serialize(token.getRefreshToken().getValue());
  150. byte[] auth = this.serialize(token.getValue());
  151. byte[] refreshToAccessKey = this.serializeKey( "refresh_to_access:" + token.getRefreshToken().getValue());
  152. conn.stringCommands().set(refreshToAccessKey, auth);
  153. byte[] accessToRefreshKey = this.serializeKey( "access_to_refresh:" + token.getValue());
  154. conn.stringCommands().set(accessToRefreshKey, refresh);
  155. if(refreshToken instanceof ExpiringOAuth2RefreshToken) {
  156. ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken)refreshToken;
  157. Date expiration = expiringRefreshToken.getExpiration();
  158. if(expiration != null) {
  159. int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue();
  160. conn.expire(refreshToAccessKey, ( long)seconds);
  161. conn.expire(accessToRefreshKey, ( long)seconds);
  162. }
  163. }
  164. }
  165. conn.closePipeline();
  166. } finally {
  167. conn.close();
  168. }
  169. }
  170. private static String getApprovalKey(OAuth2Authentication authentication) {
  171. String userName = authentication.getUserAuthentication() == null? "":authentication.getUserAuthentication().getName();
  172. return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
  173. }
  174. private static String getApprovalKey(String clientId, String userName) {
  175. return clientId + (userName == null? "": ":" + userName);
  176. }
  177. public void removeAccessToken(OAuth2AccessToken accessToken) {
  178. this.removeAccessToken(accessToken.getValue());
  179. }
  180. public OAuth2AccessToken readAccessToken(String tokenValue) {
  181. byte[] key = this.serializeKey( "access:" + tokenValue);
  182. byte[] bytes = null;
  183. RedisConnection conn = this.getConnection();
  184. try {
  185. bytes = conn.get(key);
  186. } finally {
  187. conn.close();
  188. }
  189. OAuth2AccessToken var5 = this.deserializeAccessToken(bytes);
  190. return var5;
  191. }
  192. public void removeAccessToken(String tokenValue) {
  193. byte[] accessKey = this.serializeKey( "access:" + tokenValue);
  194. byte[] authKey = this.serializeKey( "auth:" + tokenValue);
  195. byte[] accessToRefreshKey = this.serializeKey( "access_to_refresh:" + tokenValue);
  196. RedisConnection conn = this.getConnection();
  197. try {
  198. conn.openPipeline();
  199. conn.get(accessKey);
  200. conn.get(authKey);
  201. conn.del( new byte[][]{accessKey});
  202. conn.del( new byte[][]{accessToRefreshKey});
  203. conn.del( new byte[][]{authKey});
  204. List<Object> results = conn.closePipeline();
  205. byte[] access = ( byte[])(( byte[])results.get( 0));
  206. byte[] auth = ( byte[])(( byte[])results.get( 1));
  207. OAuth2Authentication authentication = this.deserializeAuthentication(auth);
  208. if(authentication != null) {
  209. String key = this.authenticationKeyGenerator.extractKey(authentication);
  210. byte[] authToAccessKey = this.serializeKey( "auth_to_access:" + key);
  211. byte[] unameKey = this.serializeKey( "uname_to_access:" + getApprovalKey(authentication));
  212. byte[] clientId = this.serializeKey( "client_id_to_access:" + authentication.getOAuth2Request().getClientId());
  213. conn.openPipeline();
  214. conn.del( new byte[][]{authToAccessKey});
  215. conn.lRem(unameKey, 1L, access);
  216. conn.lRem(clientId, 1L, access);
  217. conn.del( new byte[][]{ this.serialize( "access:" + key)});
  218. conn.closePipeline();
  219. }
  220. } finally {
  221. conn.close();
  222. }
  223. }
  224. public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
  225. byte[] refreshKey = this.serializeKey( "refresh:" + refreshToken.getValue());
  226. byte[] refreshAuthKey = this.serializeKey( "refresh_auth:" + refreshToken.getValue());
  227. byte[] serializedRefreshToken = this.serialize((Object)refreshToken);
  228. RedisConnection conn = this.getConnection();
  229. try {
  230. conn.openPipeline();
  231. conn.stringCommands().set(refreshKey, serializedRefreshToken);
  232. conn.stringCommands().set(refreshAuthKey, this.serialize((Object)authentication));
  233. if(refreshToken instanceof ExpiringOAuth2RefreshToken) {
  234. ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken)refreshToken;
  235. Date expiration = expiringRefreshToken.getExpiration();
  236. if(expiration != null) {
  237. int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue();
  238. conn.expire(refreshKey, ( long)seconds);
  239. conn.expire(refreshAuthKey, ( long)seconds);
  240. }
  241. }
  242. conn.closePipeline();
  243. } finally {
  244. conn.close();
  245. }
  246. }
  247. public OAuth2RefreshToken readRefreshToken(String tokenValue) {
  248. byte[] key = this.serializeKey( "refresh:" + tokenValue);
  249. RedisConnection conn = this.getConnection();
  250. byte[] bytes;
  251. try {
  252. bytes = conn.get(key);
  253. } finally {
  254. conn.close();
  255. }
  256. OAuth2RefreshToken var5 = this.deserializeRefreshToken(bytes);
  257. return var5;
  258. }
  259. public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
  260. this.removeRefreshToken(refreshToken.getValue());
  261. }
  262. public void removeRefreshToken(String tokenValue) {
  263. byte[] refreshKey = this.serializeKey( "refresh:" + tokenValue);
  264. byte[] refreshAuthKey = this.serializeKey( "refresh_auth:" + tokenValue);
  265. byte[] refresh2AccessKey = this.serializeKey( "refresh_to_access:" + tokenValue);
  266. byte[] access2RefreshKey = this.serializeKey( "access_to_refresh:" + tokenValue);
  267. RedisConnection conn = this.getConnection();
  268. try {
  269. conn.openPipeline();
  270. conn.del( new byte[][]{refreshKey});
  271. conn.del( new byte[][]{refreshAuthKey});
  272. conn.del( new byte[][]{refresh2AccessKey});
  273. conn.del( new byte[][]{access2RefreshKey});
  274. conn.closePipeline();
  275. } finally {
  276. conn.close();
  277. }
  278. }
  279. public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
  280. this.removeAccessTokenUsingRefreshToken(refreshToken.getValue());
  281. }
  282. private void removeAccessTokenUsingRefreshToken(String refreshToken) {
  283. byte[] key = this.serializeKey( "refresh_to_access:" + refreshToken);
  284. List<Object> results = null;
  285. RedisConnection conn = this.getConnection();
  286. try {
  287. conn.openPipeline();
  288. conn.get(key);
  289. conn.del( new byte[][]{key});
  290. results = conn.closePipeline();
  291. } finally {
  292. conn.close();
  293. }
  294. if(results != null) {
  295. byte[] bytes = ( byte[])(( byte[])results.get( 0));
  296. String accessToken = this.deserializeString(bytes);
  297. if(accessToken != null) {
  298. this.removeAccessToken(accessToken);
  299. }
  300. }
  301. }
  302. public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
  303. byte[] approvalKey = this.serializeKey( "uname_to_access:" + getApprovalKey(clientId, userName));
  304. List< byte[]> byteList = null;
  305. RedisConnection conn = this.getConnection();
  306. try {
  307. byteList = conn.lRange(approvalKey, 0L, - 1L);
  308. } finally {
  309. conn.close();
  310. }
  311. if(byteList != null && byteList.size() != 0) {
  312. List<OAuth2AccessToken> accessTokens = new ArrayList(byteList.size());
  313. Iterator var7 = byteList.iterator();
  314. while(var7.hasNext()) {
  315. byte[] bytes = ( byte[])var7.next();
  316. OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
  317. accessTokens.add(accessToken);
  318. }
  319. return Collections.unmodifiableCollection(accessTokens);
  320. } else {
  321. return Collections.emptySet();
  322. }
  323. }
  324. public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
  325. byte[] key = this.serializeKey( "client_id_to_access:" + clientId);
  326. List< byte[]> byteList = null;
  327. RedisConnection conn = this.getConnection();
  328. try {
  329. byteList = conn.lRange(key, 0L, - 1L);
  330. } finally {
  331. conn.close();
  332. }
  333. if(byteList != null && byteList.size() != 0) {
  334. List<OAuth2AccessToken> accessTokens = new ArrayList(byteList.size());
  335. Iterator var6 = byteList.iterator();
  336. while(var6.hasNext()) {
  337. byte[] bytes = ( byte[])var6.next();
  338. OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
  339. accessTokens.add(accessToken);
  340. }
  341. return Collections.unmodifiableCollection(accessTokens);
  342. } else {
  343. return Collections.emptySet();
  344. }
  345. }
  346. }

com.rui.tiger.auth.app.TigerAuthorizationServerConfig#configure(org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer)  添加token


 
 
  1. @Override
  2. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  3. endpoints.authenticationManager( this.authenticationManager)
  4. .tokenStore(redisTokenStore);
  5. }

测试下看见redis中已经有token信息 

使用jwt替换默认的token 

什么是jwt?

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,详情如下

http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

 

配置JWT

@Conditionalxxx根据条件是否实例化


 
 
  1. package com.rui.tiger.auth.app;
  2. import com.rui.tiger.auth.core.properties.SecurityProperties;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.data.redis.connection.RedisConnectionFactory;
  8. import org.springframework.security.oauth2.provider.token.TokenStore;
  9. import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
  10. import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
  11. import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
  12. /**
  13. * redis存储token
  14. * @author CaiRui
  15. * @Date 2019-05-01 17:44
  16. */
  17. @Configuration
  18. public class TokenStoreConfig {
  19. @Autowired
  20. private RedisConnectionFactory redisConnectionFactory;
  21. @Bean
  22. //这里要完全配合
  23. @ConditionalOnProperty(prefix = "tiger.auth.oauth2", name = "tokenStore", havingValue = "redis")
  24. public TokenStore redisTokenStore() {
  25. return new MyRedisTokenStore(redisConnectionFactory);
  26. }
  27. // matchIfMissing = true 如果没有找到配置项也是生效的
  28. @Configuration
  29. @ConditionalOnProperty(prefix = "tiger.auth.oauth2", name = "tokenStore", havingValue = "jwt", matchIfMissing = true)
  30. public static class JwtConfig{
  31. @Autowired
  32. private SecurityProperties securityProperties;
  33. @Bean
  34. public TokenStore jwtTokenStore() {
  35. return new JwtTokenStore(jwtAccessTokenConverter());
  36. }
  37. @Bean
  38. public JwtAccessTokenConverter jwtAccessTokenConverter() {
  39. JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
  40. //指定密签
  41. converter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey());
  42. return converter;
  43. }
  44. }
  45. }

认证服务器配置

@Autowired(required = false)
private JwtAccessTokenConverter jwtAccessTokenConverter;

我们来测试下,还是用密码模式获取

在线解析jwt的网站: http://jwt.calebb.net/

这个就是我们的jwt,解析出来的

我们用这个jwt,访问用户信息看看,成功返回

JWT自定义

分析源码可知

org.springframework.security.oauth2.provider.token.DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication, org.springframework.security.oauth2.common.OAuth2RefreshToken)

 自定义增强器的实现


 
 
  1. package com.rui.tiger.auth.app;
  2. import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
  3. import org.springframework.security.oauth2.common.OAuth2AccessToken;
  4. import org.springframework.security.oauth2.provider.OAuth2Authentication;
  5. import org.springframework.security.oauth2.provider.token.TokenEnhancer;
  6. import java.util.HashMap;
  7. import java.util.Map;
  8. /**
  9. * 自定义token增强器
  10. * @author CaiRui
  11. * @Date 2019-05-02 12:00
  12. */
  13. public class TigerJwtTokenEnhancer implements TokenEnhancer {
  14. @Override
  15. public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
  16. Map<String, Object> info = new HashMap<>();
  17. // 需要增加的信息
  18. // 所以如果是需要动态的话,只能在该方法中去调用业务方法添加动态参数信息
  19. info.put( "company", "tiger");
  20. // 设置附加信息
  21. ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);
  22. return accessToken;
  23. }
  24. }
TokenStoreConfig 配置bean

 
 
  1. @Bean
  2. @ConditionalOnBean(TokenEnhancer.class)
  3. public TokenEnhancer jwtTokenEnhancer() {
  4. return new TigerJwtTokenEnhancer();
  5. }

  认证服务器配置:

com.rui.tiger.auth.app.TigerAuthorizationServerConfig#configure(org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer)


 
 
  1. @Autowired(required = false)
  2. private TokenEnhancer jwtTokenEnhancer;
  3. @Override
  4. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  5. endpoints.authenticationManager( this.authenticationManager)
  6. .tokenStore(redisTokenStore);
  7. /**
  8. * 私有方法,但是在里面调用了accessTokenEnhancer.enhance所以这里使用链
  9. * @see DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication, org.springframework.security.oauth2.common.OAuth2RefreshToken)
  10. */
  11. if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
  12. TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
  13. List<TokenEnhancer> enhancers = new ArrayList<>();
  14. enhancers.add(jwtTokenEnhancer);
  15. enhancers.add(jwtAccessTokenConverter);
  16. enhancerChain.setTokenEnhancers(enhancers);
  17. // 一个处理链,先添加,再转换
  18. endpoints
  19. .tokenEnhancer(enhancerChain)
  20. .accessTokenConverter(jwtAccessTokenConverter);
  21. }
  22. }

发送请求,解析jwt可以看见我们的jwt增强器实现

JWT解析

添加解析jar demo项目添加

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

获取用户信息解析jwt

 


 
 
  1. @GetMapping( "me/auto")
  2. public Object getCurrentAuthentication2(Authentication authentication,HttpServletRequest request) throws UnsupportedEncodingException {
  3. String authorization = request.getHeader( "Authorization");
  4. String token = StringUtils.substringAfter(authorization, "bearer ");
  5. String jwtSigningKey = securityProperties.getOauth2().getJwtSigningKey();
  6. // 生成的时候使用的是 org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter
  7. // 源码里面把signingkey变成utf8了
  8. // JwtAccessTokenConverter类,解析出来是一个map
  9. // 所以这个自带的JwtAccessTokenConverter对象也是可以直接用来解析的
  10. byte[] bytes = jwtSigningKey.getBytes( "utf-8");
  11. Claims body = Jwts.parser().setSigningKey(bytes).parseClaimsJws(token).getBody();
  12. String company = (String) body.get( "company");
  13. log.info( "公司名称:{}",company);
  14. return body;
  15. }

测试下发送请求

 

TOKEN刷新

请求头还要带上app client信息

token如果超时,我们后台可以自动发送用户刷新token信息,提升用户体验。刷新token超时时间,建议设置长点。

 

文章转载至:https://blog.csdn.net/ahcr1026212/article/details/89737544

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值