Spring Cloud Security 整合 JWT 实现令牌存储
一、基础概述
-
在Oauth2 + Spring Cloud Security的项目环境中, Spring Cloud Security 默认把访问令牌保存在内存中。而在分布式环境中,我们可以利用JWT或Redis存储令牌信息,以便于多个服务的使用。
-
JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。
-
JWT token 的格式为 header.payload.signature:
-
header
{ "alg": "HS256", "typ": "JWT" }
-
payload
{ "exp": 1572682831, "user_name": "zhangsan", "authorities": [ "client" ], "jti": "c1a0645a-28b5-4468-b4c7-9623131853af", "client_id": "admin", "scope": [ "all" ] }
-
signature
signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败
-
-
JWT token 的官网:https://jwt.io/
二、利用Redis存储令牌
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
添加配置
server: port: 8080 servlet: context-path: / spring: application: name: oauth-redis redis: database: 0 host: 127.0.0.1 port: 6379 password:
-
添加Redis存储配置类
@Configuration public class RedisTokenStoreConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore redisTokenStore (){ return new RedisTokenStore(redisConnectionFactory); } }
-
修改认证服务器配置类
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailServiceImpl userDetailsService; @Autowired @Qualifier("redisTokenStore") private TokenStore tokenStore; /** * 使用密码模式所需配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) // 配置令牌存储策略 .tokenStore(tokenStore); } }
-
获取访问令牌
使用Postman工具
-
查看Redis信息
三、利用JWT存储令牌
-
添加JWT存储配置类
@Configuration public class JwtTokenStoreConfig { @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); // 配置JWT使用的秘钥 accessTokenConverter.setSigningKey("test_key"); return accessTokenConverter; } }
-
修改认证服务器配置类
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailServiceImpl userDetailsService; @Autowired @Qualifier("jwtTokenStore") private TokenStore tokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; /** * 使用密码模式所需配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) // 配置令牌存储策略 .tokenStore(tokenStore) .accessTokenConverter(jwtAccessTokenConverter); } }
-
获取访问令牌
使用Postman工具
-
查看JWT信息
将得到的 access_token 去https://jwt.io/ 网站进行解析
四、扩展JWT的存储内容
-
添加内容扩展类
public class JwtTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance( OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> info = new HashMap<>(); info.put("add_key", "add_value"); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); return accessToken; } }
-
修改JWT存储配置类
@Configuration public class JwtTokenStoreConfig { @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); // 配置JWT使用的秘钥 accessTokenConverter.setSigningKey("test_key"); return accessTokenConverter; } @Bean public JwtTokenEnhancer jwtTokenEnhancer() { return new JwtTokenEnhancer(); } }
-
修改认证服务器配置类
![20210325194754](C:/Users/jihongye/Documents/Scrshot/20210325194754.png@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailServiceImpl userDetailsService; @Autowired @Qualifier("jwtTokenStore") private TokenStore tokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private JwtTokenEnhancer jwtTokenEnhancer; /** * 使用密码模式所需配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> delegates = new ArrayList<>(); delegates.add(jwtTokenEnhancer); delegates.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(delegates); endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) // 配置令牌存储策略 .tokenStore(tokenStore) .accessTokenConverter(jwtAccessTokenConverter) .tokenEnhancer(enhancerChain); } }
-
查看JWT信息
五、解析JWT存储的内容
-
添加依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>
-
测试使用
public class ApplicationTest { public void test() { public void test() { String token = "jwt的token字符串"; Object obj = Jwts.parser() .setSigningKey("test_key".getBytes(StandardCharsets.UTF_8)) .parseClaimsJws(token) .getBody(); System.out.println(object); } } }
【源码地址】:GitHub