SpringSecurityOAuth2 如何自定義token信息
GitHub地址
碼雲地址
OAuth2默認的token返回最多隻攜帶瞭5個參數(client_credentials模式隻有4個 沒有refresh_token)
下面是一個返回示例:
{ "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等自定義參數:
{ "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" }
具體實現自定義token步驟如下: 新建一個自定義token信息的新建自定義token返回MyTokenEnhancer實現TokenEnhancer接口重寫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; } }
然後在認證服務配置AuthorizationServerEndpointsConfigurer中加上 MyTokenEnhancer。
這裡劃重點 因為我這裡指定瞭瞭defaultTokenServices()所以得在這個方法裡加上配置
還有,如果已經生成瞭一次沒有自定義的token信息,需要去redis裡刪除掉該token才能再次測試結果,不然你的結果一直是錯誤的,因為token還沒過期的話,是不會重新生成的。
Spring Security 使用JWT自定義Token
敘述
默認的token生成規則其實就是一個UUID,就是一個隨機的字符串,然後存到redis中去,使用JWT的話,token中可以存放一些信息,我們服務端也不需要保存這個token, 服務器通過使用保存的密鑰驗證token的正確性,隻要正確即通過驗證
使用JWT,在分佈式系統中,很好地解決瞭單點登錄問題,很容易解決瞭session共享的問題.但是是無法作廢已頒佈的令牌/不易應對數據過期,因為 token 並沒有保存到服務端, 下面來看一下如何去配置JWT
配置JWT
TokenStoreConfig 這個類中要做一些修改,之前我們隻在這個類裡面配置瞭 redis 的存儲,現在把 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; } } }
這裡首先是一個內部的靜態類 JwtTokenConfig 用來配置 JWT 的一些配置,第一個方法 jwtTokenStore() 就是配置token的存儲,然後這裡需要一個 JwtAccessTokenConverter 因為 TokenStore 隻管 Token 的存儲,生成規則還需要配置,所以 jwtAccessTokenConverter() 就是用來做一些 Token 的處理
這個類上有一個註解
@ConditionalOnProperty(prefix = "core.security.oAuth2", name="tokenStore", havingValue="jwt", matchIfMissing = true)
意思就是,在配置中有 core.security.oAuth2.tokenStore 這個配置,而且值是 jwt 的話,就生效,最後有一個 matchIfMissing = true ,這個表示, 如果配置中沒有這個配置的話,也生效
上面的 redisTokenStore 也加瞭這個註解,但是沒有 matchIfMissing 默認是 false, 總的配置就是如果在配置中沒有指定哪種 tokenStore 的話,就默認的用 jwt ,如果想要使用 redis 存儲的話,必須明確的指定 core.security.oAuth2.tokenStore: redis
最後認證服務的配置中還需要做一些修改
@Configuration @EnableAuthorizationServer public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired(required = false) private JwtAccessTokenConverter jwtAccessTokenConverter; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // ... 省略其他配置 // 隻有配置使用JWT的時候才會生效 if (jwtAccessTokenConverter != null) { endpoints.accessTokenConverter(jwtAccessTokenConverter); } } // ... 省略其他代碼 }
這裡,就是給 endpoints 指定一下 JwtAccessTokenConverter 就可以瞭
測試
請求 Token 還是跟之前的方式一樣的, 如下:
可以看到這裡的token就是使用jwt瞭
在 jwt.io 中可以把剛剛生成的token解析一下,內容如下:
這個就是我們生成的 JWT 中包含的信息
TokenEnhancer 的使用
TokenEnhancer 是一個增強器,JWT 中是可以放一些我們自定義的信息的,如果要加入一些我們自己的信息的話,就得使用 TokenEnhancer
還是修改 TokenStoreConfig ,在 JwtTokenConfig 這個內部類中,加一個配置
@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,是必須存在一個TokenEnhancer 的時候,才被創建, 之前的 JwtAccessTokenConverter 也是一個 TokenEnhancer
最後,認證服務器配置類 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的時候才會生效 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); } }
測試
獲取token,然後解析結果如下:
自定義數據解析
這裡 Spring 在解析JWT的時候會解析成一個 Authentication 對象,並不會解析我們上面設置的自定義字段,這個還需要我們自己去解析
demo 項目中增加一個 jwt 解析的依賴
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency>
然後再 /user/me 這個接口中做解析,代碼如下:
@GetMapping("/me") public Object me(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException { // 從請求頭中獲取到token String jwtToken = StringUtils.substringAfter(request.getHeader("Authorization"), AUTHORIZATION_PREFIX); log.info("請求頭中的token:{}", jwtToken); // 獲取配置中的 jwtTokenSignKey String jwtTokenSignKey = securityProperties.getOAuth2().getJwtTokenSignKey(); Claims claims = Jwts.parser().setSigningKey(jwtTokenSignKey.getBytes("UTF-8")).parseClaimsJws(jwtToken).getBody(); return claims; }
效果如下:
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Springboot開發OAuth2認證授權與資源服務器操作
- Spring Cloud OAuth2實現自定義token返回格式
- SpringSecurity OAtu2+JWT實現微服務版本的單點登錄的示例
- SpringSecurity OAuth2單點登錄和登出的實現
- Spring Security OAuth 自定義授權方式實現手機驗證碼