天天看點

oauth2自定義傳回值_Spring Boot+OAuth2,如何自定義傳回的 Token 資訊?

在本系列前面的文章中,正常情況下,OAuth2 傳回的 access_token 資訊一共包含五項:

分别是:

access_token

token_type

refresh_token

expires_in

scope

具體如下:

{

"access_token": "b9c9e345-90c9-49f5-80ab-6ce5ed5a07c9",

"token_type": "bearer",

"refresh_token": "9f843e0e-1778-495d-859a-52a1a806c150",

"expires_in": 7199,

"scope": "seller-auth"

}

但是在實際操作中,我們往往需要在這個基礎上,定制自己的傳回資訊,這就需要我們對這個東西進行自定義。本文松哥就來和大家聊一聊這裡要如何自定義。

敲黑闆劃重點: 本文還是我們最近 OAuth2 系列的延續,如果沒看過本系列之前的文章,一定先閱讀一下,這有助于更好的了解本文:

好了,不廢話了,我們來看今天的内容。

1.access_token 從哪裡來

首先我們要搞清楚,access_token 從哪裡來。

在前面的文章中,我們在生成 access_token 的時候,都配置了一個類,叫做 AuthorizationServerTokenServices,如下:

@Bean

AuthorizationServerTokenServices tokenServices() {

DefaultTokenServices services = new DefaultTokenServices();

services.setClientDetailsService(clientDetailsService());

services.setSupportRefreshToken(true);

services.setTokenStore(tokenStore);

TokenEnhancerChain chain = new TokenEnhancerChain();

chain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter,externalAccessTokenInfo));

services.setTokenEnhancer(chain);

return services;

}

在這個配置中,我們提供了一個 DefaultTokenServices 執行個體,這個執行個體就是預設生成 access_token 的工具,我們進入到 DefaultTokenServices#createAccessToken 方法中,一路追蹤,可以看到如下代碼:

private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {

DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());

int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());

if (validitySeconds > 0) {

token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));

}

token.setRefreshToken(refreshToken);

token.setScope(authentication.getOAuth2Request().getScope());

return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;

}

從這段代碼中,我們可以看到,用來儲存 access_token 的執行個體,其實就是 DefaultOAuth2AccessToken,我們再來看看 DefaultOAuth2AccessToken 的定義:

public class DefaultOAuth2AccessToken implements Serializable, OAuth2AccessToken {

private String value;

private Date expiration;

private String tokenType = BEARER_TYPE.toLowerCase();

private OAuth2RefreshToken refreshToken;

private Set scope;

private Map additionalInformation = Collections.emptyMap();

//省略其他

}

從這段屬性的聲明中,我們就可以看出來,為什麼預設傳回的資料隻有五項。

大家同時也發現,DefaultOAuth2AccessToken 中其實是提供了一個 additionalInformation 屬性用來存儲額外資訊的,但是,我們在 DefaultTokenServices 類中并沒有辦法去自定義 DefaultOAuth2AccessToken 中的屬性,也就是說,預設情況下,我們沒有辦法自己去給 additionalInformation 中添加值。

雖然預設情況下,無法添加,但是隻要大家看了上面這段源碼,就會明白,如果我們想要自定義傳回的 access_token 資訊,就要想辦法自已去定義 DefaultOAuth2AccessToken 資訊。

思路有了,接下來看操作。

2.兩種定制方案

大家知道,我們在 OAuth2 中傳回的令牌資訊分為兩大類:不透明令牌和透明令牌。

不透明令牌就是一種無可讀性的令牌,一般來說就是一段普通的 UUID 字元串。不透明令牌的最大問題在于會降低系統性能和可用性,并且增加延遲(因為必須遠端校驗令牌)。

透明令牌的典型代表就是 JWT 了,使用者資訊都儲存在 JWT 字元串中,關于 JWT 的資訊,大家可以參考這篇文章:想讓 OAuth2 和 JWT 在一起愉快玩耍?請看松哥的表演。

在實際開發中,大部分情況下,我們的 OAuth2 都是搭配 JWT 一起來使用的,是以,這裡我就主要講一下在生成的 JWT 中如何定制傳回資訊。

如果我們使用了 OAuth2+JWT 的方案,那正常情況下,我們還需要配置一個 JwtAccessTokenConverter 的執行個體(參考:想讓 OAuth2 和 JWT 在一起愉快玩耍?請看松哥的表演),JWT 字元串将由 JwtAccessTokenConverter 執行個體負責生成。

JwtAccessTokenConverter 執行個體生成 JWT 的方法是在上文列出來的 DefaultTokenServices#createAccessToken 方法之後執行,該方法最後有一句:

accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;

這就是說,如果提供了 accessTokenEnhancer ,就進入到 accessTokenEnhancer 的 enhance 方法中對 access_token 做二次處理,accessTokenEnhancer 則就是我們的 JwtAccessTokenConverter 執行個體。

從這裡大家看到,想要自定義 Token 資訊,我們有兩個時機,第一個時機就是在 DefaultTokenServices#createAccessToken 方法中修改,但是工作量較大,不推薦;第二個時機是在進入到 JwtAccessTokenConverter#enhance 方法之後修改,這是目前比較可行的方法。

如果采用第二種方案,就需要我們自定義一個類繼承自 JwtAccessTokenConverter,如下:

public class MyJwt extends JwtAccessTokenConverter {

@Override

public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {

Map additionalInformation = new LinkedHashMap<>();

Map info = new LinkedHashMap<>();

info.put("author", "江南一點雨");

info.put("email", "[email protected]");

info.put("site", "www.javaboy.org");

info.put("weixin", "a_java_boy2");

info.put("WeChat Official Accounts", "江南一點雨");

info.put("GitHub", "https://github.com/lenve");

info.put("user", SecurityContextHolder.getContext().getAuthentication().getPrincipal());

additionalInformation.put("info", info);

((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);

return super.enhance(accessToken, authentication);

}

}

在這裡,我們自定義 MyJwt 繼承自 JwtAccessTokenConverter 并重寫 enhance 方法:

首先我們構造自己的附加資訊,如果如需要目前登入使用者資訊,可以從 SecurityContextHolder 中擷取。

将附加資訊放到 OAuth2AccessToken 的 additionalInformation 屬性中去。

這樣相當于我們就修改了預設生成的 DefaultOAuth2AccessToken 了,然後再把修改後的 DefaultOAuth2AccessToken 執行個體調用 super.enhance 方法去生成 jwt 字元串,這樣生成的 jwt 字元串就有我們的自定義資訊了。

最後,在 TokenConfig 中配置 MyJwt 的執行個體,如下:

@Configuration

public class TokenConfig {

@Bean

TokenStore tokenStore() {

return new JwtTokenStore(jwtAccessTokenConverter());

}

@Bean

JwtAccessTokenConverter jwtAccessTokenConverter() {

JwtAccessTokenConverter converter = new MyJwt();

converter.setSigningKey("www.javaboy.org");

return converter;

}

}

配置完成後,其他地方的代碼不變(參考:想讓 OAuth2 和 JWT 在一起愉快玩耍?請看松哥的表演),我們啟動項目來生成登入的 access_token 資訊。

3.測試

接下來,我們啟動項目進行測試:

oauth2自定義傳回值_Spring Boot+OAuth2,如何自定義傳回的 Token 資訊?

可以看到,此時生成的 jwt 字元串就比較長了,我們将 access_token 拿到 /oauth/check_token 去校驗一下就知道生成的具體資訊了,如下:

oauth2自定義傳回值_Spring Boot+OAuth2,如何自定義傳回的 Token 資訊?

可以看到,我們已經成功的将自定義資訊存入 jwt 字元串中了。

當然,還有一種情況就是你可能隻是想在調用 /oauth/token 接口的時候添加一些額外資訊,并不想将額外資訊添加到 jwt 中,就是下面這種效果:

oauth2自定義傳回值_Spring Boot+OAuth2,如何自定義傳回的 Token 資訊?

4.擴充

好了,前面雖然跟大家分享的是 OAuth2+JWT 如何生成自定義的 access_token 資訊,但是相信大家看完之後,應該也會針對不透明令牌生成自定義資訊。

我這裡也和大家分享一下思路:

上面代碼的核心思路,就是在從 DefaultTokenServices#createAccessToken 方法到 JwtAccessTokenConverter#enhance 方法的過程中,給 DefaultOAuth2AccessToken 對象的 additionalInformation 屬性添加了附加資訊。

而 JwtAccessTokenConverter 是 TokenEnhancer 的執行個體,是以如果我們想要定制不透明令牌的資訊,隻需要自己定義類實作 TokenEnhancer 接口,并且在 enhance 方法中添加附加資訊即可。這個思路給大家,小夥伴們可以自行嘗試一下。

好了,今天就和大家分享這麼多。如果覺得有收獲,記得點個在看鼓勵下松哥哦~

最後再說一下,如果你覺得閱讀本文有些吃力,一定閱讀一下本系列前面的文章。

關注微信公衆号江南一點雨,回複 2020 擷取 SpringSecurity+OAuth2幹貨!