SpringSecurityOAuth2 如何自定義token信息
OAuth2默認(rèn)的token返回最多只攜帶了5個(gè)參數(shù)(client_credentials模式只有4個(gè) 沒(méi)有refresh_token)
下面是一個(gè)返回示例:
{
"access_token": "1e93bc23-32c8-428f-a126-8206265e17b2",
"token_type": "bearer",
"refresh_token": "0f083e06-be1b-411f-98b0-72be8f1da8af",
"expires_in": 3599,
"scope": "auth api"
}
然后我們需要的token可能需要增加username等自定義參數(shù):
{
"access_token": "1e93bc23-32c8-428f-a126-8206265e17b2",
"token_type": "bearer",
"refresh_token": "0f083e06-be1b-411f-98b0-72be8f1da8af",
"expires_in": 3599,
"scope": "auth api",
"username":"username"
}
具體實(shí)現(xiàn)自定義token步驟如下: 新建一個(gè)自定義token信息的新建自定義token返回MyTokenEnhancer實(shí)現(xiàn)TokenEnhancer接口重寫(xiě)enhance方法:
/**
* @Description 自定義token返回值
* @Author wwz
* @Date 2019/07/31
* @Param
* @Return
*/
public class MyTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("username", user.getUsername());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
然后在認(rèn)證服務(wù)配置AuthorizationServerEndpointsConfigurer中加上 MyTokenEnhancer。
這里劃重點(diǎn) 因?yàn)槲疫@里指定了了defaultTokenServices()所以得在這個(gè)方法里加上配置

還有,如果已經(jīng)生成了一次沒(méi)有自定義的token信息,需要去redis里刪除掉該token才能再次測(cè)試結(jié)果,不然你的結(jié)果一直是錯(cuò)誤的,因?yàn)閠oken還沒(méi)過(guò)期的話,是不會(huì)重新生成的。
Spring Security 使用JWT自定義Token
敘述
默認(rèn)的token生成規(guī)則其實(shí)就是一個(gè)UUID,就是一個(gè)隨機(jī)的字符串,然后存到redis中去,使用JWT的話,token中可以存放一些信息,我們服務(wù)端也不需要保存這個(gè)token, 服務(wù)器通過(guò)使用保存的密鑰驗(yàn)證token的正確性,只要正確即通過(guò)驗(yàn)證
使用JWT,在分布式系統(tǒng)中,很好地解決了單點(diǎn)登錄問(wèn)題,很容易解決了session共享的問(wèn)題.但是是無(wú)法作廢已頒布的令牌/不易應(yīng)對(duì)數(shù)據(jù)過(guò)期,因?yàn)?token 并沒(méi)有保存到服務(wù)端, 下面來(lái)看一下如何去配置JWT
配置JWT
TokenStoreConfig 這個(gè)類(lèi)中要做一些修改,之前我們只在這個(gè)類(lèi)里面配置了 redis 的存儲(chǔ),現(xiàn)在把 JWT 的配置也加上,如下:
@Configuration
public class TokenStoreConfig {
@Autowired
private RedisConnectionFactory connectionFactory;
@Bean
@ConditionalOnProperty(prefix = "core.security.oAuth2", name="tokenStore", havingValue="redis")
public TokenStore redisTokenStore(){
return new RedisTokenStore(connectionFactory);
}
@Configuration
@ConditionalOnProperty(prefix = "core.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(){
// token生成中的一些處理
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(securityProperties.getOAuth2().getJwtTokenSignKey());
return converter;
}
}
}
這里首先是一個(gè)內(nèi)部的靜態(tài)類(lèi) JwtTokenConfig 用來(lái)配置 JWT 的一些配置,第一個(gè)方法 jwtTokenStore() 就是配置token的存儲(chǔ),然后這里需要一個(gè) JwtAccessTokenConverter 因?yàn)?TokenStore 只管 Token 的存儲(chǔ),生成規(guī)則還需要配置,所以 jwtAccessTokenConverter() 就是用來(lái)做一些 Token 的處理
這個(gè)類(lèi)上有一個(gè)注解
@ConditionalOnProperty(prefix = "core.security.oAuth2", name="tokenStore", havingValue="jwt", matchIfMissing = true)
意思就是,在配置中有 core.security.oAuth2.tokenStore 這個(gè)配置,而且值是 jwt 的話,就生效,最后有一個(gè) matchIfMissing = true ,這個(gè)表示, 如果配置中沒(méi)有這個(gè)配置的話,也生效
上面的 redisTokenStore 也加了這個(gè)注解,但是沒(méi)有 matchIfMissing 默認(rèn)是 false, 總的配置就是如果在配置中沒(méi)有指定哪種 tokenStore 的話,就默認(rèn)的用 jwt ,如果想要使用 redis 存儲(chǔ)的話,必須明確的指定 core.security.oAuth2.tokenStore: redis
最后認(rèn)證服務(wù)的配置中還需要做一些修改
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired(required = false)
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// ... 省略其他配置
// 只有配置使用JWT的時(shí)候才會(huì)生效
if (jwtAccessTokenConverter != null) {
endpoints.accessTokenConverter(jwtAccessTokenConverter);
}
}
// ... 省略其他代碼
}
這里,就是給 endpoints 指定一下 JwtAccessTokenConverter 就可以了
測(cè)試
請(qǐng)求 Token 還是跟之前的方式一樣的, 如下:

可以看到這里的token就是使用jwt了
在 jwt.io 中可以把剛剛生成的token解析一下,內(nèi)容如下:

這個(gè)就是我們生成的 JWT 中包含的信息
TokenEnhancer 的使用
TokenEnhancer 是一個(gè)增強(qiáng)器,JWT 中是可以放一些我們自定義的信息的,如果要加入一些我們自己的信息的話,就得使用 TokenEnhancer
還是修改 TokenStoreConfig ,在 JwtTokenConfig 這個(gè)內(nèi)部類(lèi)中,加一個(gè)配置
@Bean
@ConditionalOnBean(TokenEnhancer.class)
public TokenEnhancer jwtTokenEnhancer(){
return new MyJwtTokenEnhancer();
}
然后 MyJwtTokenEnhancer 代碼如下:
public class MyJwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> info = new HashMap<>(1);
info.put("custom", "test");
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
return accessToken;
}
}
這里加了 @ConditionalOnBean,是必須存在一個(gè)TokenEnhancer 的時(shí)候,才被創(chuàng)建, 之前的 JwtAccessTokenConverter 也是一個(gè) TokenEnhancer
最后,認(rèn)證服務(wù)器配置類(lèi) MyAuthorizationServerConfig 中,需要修改一下
@Autowired(required = false)
private TokenEnhancer jwtTokenEnhancer;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.userDetailsService(userDetailsService);
endpoints.tokenStore(tokenStore);
// 只有配置使用JWT的時(shí)候才會(huì)生效
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);
}
}
測(cè)試
獲取token,然后解析結(jié)果如下:

自定義數(shù)據(jù)解析
這里 Spring 在解析JWT的時(shí)候會(huì)解析成一個(gè) Authentication 對(duì)象,并不會(huì)解析我們上面設(shè)置的自定義字段,這個(gè)還需要我們自己去解析
demo 項(xiàng)目中增加一個(gè) jwt 解析的依賴
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
然后再 /user/me 這個(gè)接口中做解析,代碼如下:
@GetMapping("/me")
public Object me(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException {
// 從請(qǐng)求頭中獲取到token
String jwtToken = StringUtils.substringAfter(request.getHeader("Authorization"), AUTHORIZATION_PREFIX);
log.info("請(qǐng)求頭中的token:{}", jwtToken);
// 獲取配置中的 jwtTokenSignKey
String jwtTokenSignKey = securityProperties.getOAuth2().getJwtTokenSignKey();
Claims claims = Jwts.parser().setSigningKey(jwtTokenSignKey.getBytes("UTF-8")).parseClaimsJws(jwtToken).getBody();
return claims;
}
效果如下:

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
RestTemplate報(bào)錯(cuò)400 Bad Request的解決方案
在使用Spring Boot時(shí),若直接通過(guò)@Autowired注入RestTemplate可能會(huì)遇到400BadRequest錯(cuò)誤,原因在于Spring Boot官方文檔指出,由于RestTemplate實(shí)例通常需要在使用前進(jìn)行定制,因此Spring Boot不會(huì)自動(dòng)配置單個(gè)RestTemplate Bean2024-11-11
Spring中@Autowired自動(dòng)注入map詳解
這篇文章主要介紹了Spring中@Autowired自動(dòng)注入map詳解, spring是支持基于接口實(shí)現(xiàn)類(lèi)的直接注入的,支持注入map,list等集合中,不用做其他的配置,直接注入,需要的朋友可以參考下2023-10-10
IntelliJ IDEA 2018 最新激活碼(截止到2018年1月30日)
這篇文章主要介紹了IntelliJ IDEA 2018 最新激活碼(截止到2018年1月30日)的相關(guān)資料,需要的朋友可以參考下2018-01-01
從千千靜聽(tīng)歌詞服務(wù)器獲取lrc歌詞示例分享
這篇文章主要介紹了使用PHP從千千靜聽(tīng)歌詞服務(wù)器獲取lrc歌詞的方法,大家參考使用吧2014-01-01
SparkSQL中的JSON內(nèi)置函數(shù)全解析
你是否曾經(jīng)為處理JSON數(shù)據(jù)而頭疼?SparkSQL為我們提供了強(qiáng)大的內(nèi)置JSON函數(shù),讓JSON處理變得輕而易舉,本文將帶你深入了解這些函數(shù),感興趣的朋友一起看看吧2024-08-08
SpringBoot整合Hmily實(shí)現(xiàn)TCC分布式事務(wù)
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何整合Hmily實(shí)現(xiàn)TCC分布式事務(wù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-11-11

