6.9 使用JWT替换默认令牌
6-9 使用JWT替换默认令牌
JWT(Json Web Token)三个特点
1、自包含:包含了一些跟项目相关的信息,可以被解析;
2、密签:可以用密钥来签名,签名指的是防止别人去篡改数据;
3、可扩展:自包含的信息可根据自己的需求扩展需要存放的信息;
6.9.1、配置TokenConfig类
在TokenConfig类中添加一个内部配置类JwtTokenConfig ;
package com.moss.securityapp;
import com.moss.securityapp.jwt.MossJwtTokenEnhancer;
import com.moss.securitycore.properties.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
/**
* Token保存配置
*
* @author lwj
*/
@Configuration
public class TokenStoreConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
/**
* redisTokenStore配置,下面注解中表示当在application.yml中配置了tokenStore属性的时候才启用
* @return
*/
@Bean
@ConditionalOnProperty(prefix = "moss.security.oauth2", name = "tokenStore", havingValue = "redis")
public TokenStore redisTokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
/**
* matchIfMissing :当tokenStore没有值的时候是否生效(不在application.yml文件中配置tokenStore这个属性的时候默认触发)
* 当tokenStore = jwt的时候或则tokenStore没有配置的时候使用下面的配置
*/
@Configuration
@ConditionalOnProperty(prefix = "moss.security.oauth2", name = "tokenStore", havingValue = "jwt", matchIfMissing = true)
public static class JwtTokenConfig {
@Autowired
private SecurityProperties securityProperties;
@Bean
public TokenStore JwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
// 设置密签
accessTokenConverter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey());
return accessTokenConverter;
}
@Bean
@ConditionalOnMissingBean(name = "jwtTokenEnhancer")
public TokenEnhancer jwtTokenEnhancer() {
return new MossJwtTokenEnhancer();
}
}
}
在app项目中添加MossJwtTokenEnhancer
package com.moss.securityapp.jwt;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap;
import java.util.Map;
/**
* jwt的token增强器
* @author lwj
*/
public class MossJwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
Map<String, Object> info = new HashMap<>();
info.put("company", "moss");
// 设置附加信息
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
return oAuth2AccessToken;
}
}
在core项目的OAuth2Properties类中添加jwtToken的签名密钥;
private String jwtSigningKey = "moss";
6.9.2、修改认证服务器(MossAuthorizationServerConfig)
添加jwttoken的生成器,和增强器;
@Autowired(required = false)
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired(required = false)
private TokenEnhancer jwtTokenEnhancer;
修改该类中的configure方法
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(redisTokenStore)
.authenticationManager(authenticationManager);
// 如果在spring容器中能够找到jwt的token生成器的话则用jwt的
if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> enhancers = new ArrayList<>();
enhancers.add(jwtTokenEnhancer);
enhancers.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(enhancers);
// 一个处理链,先添加,再转换
endpoints
.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
}
备注:application.yml文件中的配置说明;注释的“tokenStore”属性与TokenStoreConfig类中的注解相关,具体看该类中的注释;
6.9.3、测试JwtToken
获取jwttoken
返回结果
作为自包含的token的特性,我们可以将token值复制到jwt在线解析上去解析系统生成的token;
接下来我们通过拿到的jwtToken来试着请求下用户信息
我们发现返回的body中没有值,也就是说我们的UserController中的方法没有接收到值
6.9.4、解析使用jwtToken的请求方法
6.9.4.1、在demo项目的pom.xml文件中添加解析依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
6.9.4.1、修改UserController中的me方法
/**
* 获取用户信息
*
* @param user @AuthenticationPrincipal UserDetails 原来的接收参数,现在改成了Authentication,实际还需要解析处理
* @return
*/
@GetMapping("/me")
public Object getCurrentUser(Authentication user, HttpServletRequest request) throws UnsupportedEncodingException {
String header = request.getHeader("Authorization");
String token = StringUtils.substringAfter(header, "bearer ");
// 由于在写入的时候用的是另一个
Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("utf-8"))
.parseClaimsJws(token).getBody();
/** 解析token增强器中补充的字段company
* {@link MossJwtTokenEnhancer#enhance(OAuth2AccessToken, OAuth2Authentication)} */
String company = (String) claims.get("company");
log.info("-->{}", company);
return user;
}
6.9.2.4、重启服务测试
response中的body中的部分内容为全部的内容,具体需要可自行修改解析部分和增强器部分的代码;
至此,使用Jwt替换默认的token令牌的配置就完结了。