JWT(Json Web Token)是Json开放的一个Token标准,从这个名字来看它是一个令牌(Token)的一个标准。
DefaultTokenServices
特点:
- 自包含:生成的字符串是包含特定信息的,在做身份权限校验的时候直接解析字符串就能获取当前用户身份权限信息,不需要在存储中在读取校验了
- 密签:可以指定密钥签名,但是不建议将总要数据放入JWT签名串中,因为还是能的,这个JWT不是加密,只是签名,防止被篡改,JWT是一套开放的标准,那么别人也能使用这套标准获取用户信息
- 可扩展:可以自己放入一些数据生成签名
代码编写
1.编写JWT配置类
@Configuration
public class JwtConfig {
//配置token的存储
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
//配置token的生成
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 指定签名的秘钥
converter.setSigningKey("yiyi");
return converter;
}
}
或者
//使用内存存储令牌/二选一
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
//使用Redis存储令牌/二选一
@Bean
@ConditionalOnProperty(prefix = "yy.security.oauth2", name = "tokenStore", havingValue = "redis", matchIfMissing = true)
public TokenStore tokenStore() {
RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
tokenStore.setPrefix(PROJECT_OAUTH_ACCESS);//设置Redis中OAuth相关前缀
return tokenStore;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
return converter;
}
这里需要注意指定的密钥,需要保存好,后面检验JWT签名的时候还需要这个密钥来检验签名!
2.使用JWT生成令牌策略
在认证服务配置类中加入下面代码
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain enhancerChain=new TokenEnhancerChain();
List<TokenEnhancer> enhancers=new ArrayList<>();
enhancers.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(enhancers);
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
或者
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {//配置令牌的访问端点和令牌服务
endpoints
.authorizationCodeServices(authorizationCodeServices)
.authenticationManager(authenticationManager)//认证管理器
.tokenServices(tokenService())
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
;
}
//令牌管理服务
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setSupportRefreshToken(true);//支持刷新令牌
service.setTokenStore(tokenStore);//令牌存储策略
service.setAccessTokenValiditySeconds(6666); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
//令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
service.setTokenEnhancer(tokenEnhancerChain);
return service;
}
3.重启服务器访问测试
看看这里JWT中都包含了那些用户信息
JWT解析官网 这里可以看出包含了用户基本信息,身份权限信息,还包括客户端色secret4.通过JWT令牌访问接口
这里访问加上JWT签名串即可请求后端接口
5.通过JWT令牌获取用户信息
在之前使用默认的token时controller中写法如下
//获取普通token数据
@GetMapping("/getDetails")
public Object getDetails(@AuthenticationPrincipal UserDetails user){
return user;
}
但是这里并不使用JWT模式,JWT获取用户信息写法如下
//获取默认JWT数据
@GetMapping("/getJWT")
public Object getJWT(Authentication user){
return user;
}
在之前有提到一个可扩展的特点,那么这里其实也就是加入我们自己的数据生成JWT签名!
6.JWT添加扩展字段
添加JWT增强器
@Slf4j
public class TokenJwtEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, String> requestParameters = authentication.getOAuth2Request().getRequestParameters();//获取端点上下文信息
log.info("Client===>"+requestParameters.toString());//端点信息
Authentication userAuthentication = authentication.getUserAuthentication();//登录用户信息
log.info("username===>" + userAuthentication.getName());
// info是要往JWT中放入的信息
Map<String, Object> info = new HashMap<>();
info.put("r", "yiyi");
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);//设置附加信息
return accessToken;
}
}
7.Token生成策略中配置JWT增强器
public class JwtConfig {
//配置token的存储
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
//配置token的生成
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 指定签名的秘钥
converter.setSigningKey("yiyi");
return converter;
}
//JWT添加自定义数据
@Bean
public TokenEnhancer jwtTokenEnhancer(){
return new TokenJwtEnhancer();
}
}
8.认证服务核心配置添加JWT增强器
@Autowired
private TokenEnhancer jwtTokenEnhancer;//自定义jwt生成数据模板
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager);
endpoints.userDetailsService(userDetailsService);
TokenEnhancerChain enhancerChain=new TokenEnhancerChain();
List<TokenEnhancer> enhancers=new ArrayList<>();
enhancers.add(jwtTokenEnhancer);
enhancers.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(enhancers);
endpoints
.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);//将普通的token转换为jwt
}
9.重启访问测试
得到而外数据r
解析成功!那在从接口里获取一下用户信息试试!
发现并没有包含我们自己的扩展字段,这是因为默认的获取用户相关信息是无法获取扩展字段的
@GetMapping("/getJWT")
public Object getJWT(Authentication user){
return user;
}
10.获取扩展字段
导入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
这里不难看出其实这个依赖也就是解析网站用的一样!
编写获取扩展信息controller
//获取自定义JWT解析数据
@GetMapping("/myJWT")
public Object getmyJWT(Authentication user, HttpServletRequest request) throws UnsupportedEncodingException {
String header=request.getHeader("Authorization");
String token= StringUtils.substringAfter(header,"bearer");
Claims claims= Jwts.parser().setSigningKey("yiyi".getBytes("UTF-8"))
//getBytes("UTF-8")这里设置是因为这里和JwtAccessTokenConverter生成的编码格式不一致,JwtAccessTokenConverter为UTF-8,而这里使用的不是UTF-8,所以需要设置一下
.parseClaimsJws(token).getBody();
String r=(String)claims.get("r");
log.info("r===>"+r);
return user;
}
、
获取成功!