org.springframework.security.oauth2.provider.token.DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication, org.springframework.security.oauth2.common.OAuth2RefreshToken)
可见token根据UUID生成的
所以token里面的信息是无意义的
看上节redis里面token的存储,是把相关信息与token做了关联
如过redis服务器挂掉了,那么这个token就是毫无意义的,以为他本身不包含任何信息
jwt 恰恰相反,它是自包含了相关的信息
密签的意思 不是 jwt 信息的加密或解密
他是指如果 jwt 里面的信息被修改以后 我们能够通过密签发现
jwt不能存储敏感信息
可以在里面放任何数据
这个配置里面配置了两个TokenStore
根据不同的配置确认token的类型
默认是JwtTokenStore
@Configuration
public class TokenStoreConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
@ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "redis",matchIfMissing = false)
public TokenStore tokenStore(){
return new RedisTokenStore(redisConnectionFactory);
}
@Configuration
@ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "jwt",matchIfMissing = true)
//prefix 是只application配置文件中 配置的最后一个点分隔后前面的一部分 这叫前缀
//name 对应的是后缀 最后一个点分隔后的后面的那部分
//即检查的配置项为 whale.security.oauth2.storeType
//havingValue = "jwt" 当配置项的值为jwt的时候 这个类里面的所有配置生效
//matchIfMissing 如果配置文件里没有找到这个属性,即默认生效
public static class JwtTokenConfig{
@Autowired
private SecurityProperties securityProperties;
//token 的存储
@Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
//token生成处理
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey());
return jwtAccessTokenConverter;
}
}
}
/**
* 这个是秘钥 一定要保存好
*
* 发出去的令牌要用它签名
* 收到的令牌也要用它来验签
*
* jwt 是token的标准协议,唯一的安全性就是这个秘钥
*
*/
private String jwtSigningKey = "whale";
用表单登录获取token的方式
发现 这次的 access_token 很长
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTg2MDg5NjQsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiMzYzNjJiZWUtMjFjOS00ZWM3LWIxMmUtYjQyYzYyYzViZjI5IiwiY2xpZW50X2lkIjoid2hhbGUiLCJzY29wZSI6WyJhbGwiLCJyZWFkIiwid3JpdGUiXX0.PJ1vcPCccfVfrHxCJzRs19xoFV6hnu5lMetJuYARylw",
WhaleTokenEnhancer
public class WhaleTokenEnhancer implements TokenEnhancer {
/**
*
* @param oAuth2AccessToken
* @param oAuth2Authentication
* @return
*/
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
Map info = new HashMap<>();
info.put("company","埃森哲");
((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);
return oAuth2AccessToken;
}
}
·······························
public static class JwtTokenConfig{
·····················
@Bean
@ConditionalOnMissingBean(name = "jwtTokenEnhancer")
public TokenEnhancer jwtTokenEnhancer(){
return new WhaleTokenEnhancer();
}
}
部分代码如下
@Autowired(required = false)
private TokenEnhancer jwtTokenEnhancer;
/**
*
* @param endpoints oauth/token 的入口点
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// super.configure(endpoints);
// endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
if(jwtAccessTokenConverter!=null && jwtTokenEnhancer!=null){
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List enhancers = new ArrayList<>();
enhancers.add(jwtTokenEnhancer);
enhancers.add(jwtAccessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(enhancers);
endpoints.tokenEnhancer(tokenEnhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
}
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCIsInJlYWQiLCJ3cml0ZSJdLCJjb21wYW55Ijoi5ZD5qOu5ZOyIiwiZXhwIjoxNTU4NjEzNjQzLCJhdXRob3JpdGllcyI6WyJhZG1pbiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI5OTA3NWZjOC04MTI3LTQ2YWYtYTM2OS0wNTlkODlkNTIwMTEiLCJjbGllbnRfaWQiOiJ3aGFsZSJ9.1Ild0mbgxQn1No0NQIQI2gWrTDFDcgdHYHdC_kdGq_w
解析如下
注意 虽然我们的jwt可以携带额外信息
但是 我们的spring 只是 将 jwt规范里面的信息解析成 Authentication
如果我们需要jwt token 里的额外信息呢?
为此我们需要加入新的依赖
https://jwt.io/
https://www.jsonwebtoken.io/
试了几个 0.9.0 版本可以
io.jsonwebtoken
jjwt
0.9.0
@GetMapping("/me11")
public Object getCurrentUser11(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException {
//spring 会自动找到Authentication类型的数据注入
String header = request.getHeader("Authorization");
String token = StringUtils.substringAfter(header, "Bearer ");
//需要用秘钥来验证签名
//注意 签名的时候 默认用的是秘钥的utf-8编码 ,同时验签的时候也要知道 编码 utf-8
Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("utf-8"))
.parseClaimsJws(token).getBody();
String company = (String) claims.get("company");
System.out.println(company);
return authentication;
}
在用户无感的情况下用refresh_token得到一个新的token
但我的refresh_token没有测试成功
也不知道什么原因
按道理是完全可以的,也不知道问题出在哪
https://auth0.com/docs/tokens/refresh-token/current#9e51c5c22d4b450cab209018dc3be588_java
Use a Refresh Token
To exchange the Refresh Token you received during authorization for a new Access Token, make a POST request to the /oauth/token endpoint in the Authentication API, using grant_type=refresh_token.
curl --request POST \
--url 'https://YOUR_DOMAIN/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=refresh_token&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&refresh_token=YOUR_REFRESH_TOKEN'
https://openapi.baidu.com/wiki/index.php?title=使用Refresh_Token获取Access_Token
https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
https://auth0.com/docs/tokens/refresh-token/current
https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#acquiring--client-ids-and-secrets