[總結]spring security oauth2認證回執源碼分析

目錄

1.回執

回執版本一(默認)

回執版本二

2.回執DefaultOAuth2AccessToken類圖

3.請求/oauth/token流程

3.1.CompositeTokenGranter.grant

3.1.1根據前端傳參的grantType,匹配數組tokenGranters中的對應類型

3.1.2.validateGrantType看ClientDetails是否有對應的grantType的權限

3.1.3.獲取用戶token

3.2.返回token

4.兩個版本回執的原因

4.1.OAuth2AccessToken的序列化方式有兩種

4.2.版本1回執OAuth2AccessTokenJackson2Serializer

4.3.版本2回執OAuth2AccessTokenJackson1Serializer

4.4.回執版本變化的原因

4.5.fastJSON使用默認回執數據結構


 

 

1.回執

回執版本一(默認)

* {
*     "access_token": "eeff86b8-66d5-428c-9c2b-9d1cce4d502a",
*     "token_type": "bearer",
*     "refresh_token": "7cc00001-ba52-473f-9c49-ee63f2ca1a0c",
*     "expires_in": 1799,
*     "scope": "read write trust"
* }

回執版本二

DefaultOAuth2AccessToken

* {
*  "additionalInformation": {},
*  "expiration": "2020-01-13 16:52:45",
*  "expired": false,
*  "expiresIn": 1598,
*  "refreshToken": {
*     "expiration": "2020-01-13 17:22:45",
*     "value": "a8ddb0a0-5872-4b70-aa12-3f0a4c4e9247"
*        },
*  "scope": [
*     "read",
*     "write",
*     "trust"
*  ],
*  "tokenType": "bearer",
*  "value": "2b576b11-cad8-4756-89e6-7090023407b2"
*}

2.回執DefaultOAuth2AccessToken類圖

 

 

 

3.請求/oauth/token流程

3.1.CompositeTokenGranter.grant

 

3.1.1根據前端傳參的grantType,匹配數組tokenGranters中的對應類型

 

 

 

granter.grant()方法如果grantType不匹配,就直接返回null。如果匹配就validateGrantType

 

3.1.2.validateGrantType看ClientDetails是否有對應的grantType的權限

 

 

這個set的值,是我們根據用戶去在數據庫保存的。

 

 

3.1.3.獲取用戶token

 

 

先從tokenStore獲取有沒有token,如果token存在,則校驗是否過期,過期了則先清除刷新token,再清除該token。

 

有5種getAccessToken方式,我們如果用的是數據庫,就是JdbcTokenStore

如果沒有已經存在的token,則新建

 

 

 

新建的token類型正是DefaultOAuth2AccessToken

 

3.2.返回token

返回結果會通過通用的項目的WebMvcConfigurerAdapter進行序列化(因爲實際上也是http請求的回執)

 

 

4.兩個版本回執的原因

4.1.OAuth2AccessToken的序列化方式有兩種

OAuth2AccessTokenJackson1Serializer

OAuth2AccessTokenJackson2Serializer

 

4.2.版本1回執OAuth2AccessTokenJackson2Serializer

 

{
    "access_token": "2de139d5-4b8e-49d0-baf4-1f37a9b3796d",
    "token_type": "bearer",
    "refresh_token": "f35dae51-c6d3-4a10-86b7-5f417834baa4",
    "expires_in": 1793,
    "scope": "read write trust"
}

 

4.3.版本2回執OAuth2AccessTokenJackson1Serializer

 

{
    "additionalInformation": {},
    "expiration": "2020-01-14 11:07:49",
    "expired": false,
    "expiresIn": 1798,
    "refreshToken": {
        "expiration": "2020-01-14 11:37:49",
        "value": "47ad8c7b-61f9-41db-bb7c-8a020efc38a1"
    },
    "scope": [
        "read",
        "write",
        "trust"
    ],
    "tokenType": "bearer",
    "value": "6a49bff9-2f70-41cb-b83b-209294c45c0c"
}

 

 

4.4.回執版本變化的原因

Spring Security Oauth2 返回非標準數據結構 OAuth2AccessToken 序列化問題_yuelangyc的專欄-CSDN博客 https://blog.csdn.net/yuelangyc/article/details/88235639

 

如文章所述,如果自定義了消息處理,覆蓋了默認的消息處理器。

 

使用 fastjson 時, spring security oauth2 獲取 token 格式變化 · Issue #1640 · alibaba/fastjson https://github.com/alibaba/fastjson/issues/1640

 

針對fastjson,確實存在自定義fastjson消息處理器時,出現回執版本變化。

 

4.5.fastJSON使用默認回執數據結構

參考上面的文章

 

你需要自行寫個HttpMessageConverter來轉換OAuth2AccessToken,fastjson本身也意識到這個問題,但是FormOAuth2AccessTokenMessageConverter這個類的writeInternal方法沒有進行實現,你需要自行實現轉換

public class OAuth2AccessTokenMessageConverter extends AbstractHttpMessageConverter<OAuth2AccessToken> {

    private final FastJsonHttpMessageConverter delegateMessageConverter;

    public OAuth2AccessTokenMessageConverter() {
        super(MediaType.APPLICATION_JSON);
        this.delegateMessageConverter = new FastJsonHttpMessageConverter();
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return OAuth2AccessToken.class.isAssignableFrom(clazz);
    }

    @Override
    protected OAuth2AccessToken readInternal(Class<? extends OAuth2AccessToken> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        throw new UnsupportedOperationException(
                "This converter is only used for converting from externally acquired form data");
    }

    @Override
    protected void writeInternal(OAuth2AccessToken accessToken, HttpOutputMessage outputMessage) throws IOException,
            HttpMessageNotWritableException {
        Map<String, Object> data = new HashMap<>(8);
        data.put(OAuth2AccessToken.ACCESS_TOKEN, accessToken.getValue());
        data.put(OAuth2AccessToken.TOKEN_TYPE, accessToken.getTokenType());
        data.put(OAuth2AccessToken.EXPIRES_IN, accessToken.getExpiresIn());
        data.put(OAuth2AccessToken.SCOPE, String.join(" ", accessToken.getScope()));
        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        if (Objects.nonNull(refreshToken)) {
            data.put(OAuth2AccessToken.REFRESH_TOKEN, refreshToken.getValue());
        }
        delegateMessageConverter.write(data, MediaType.APPLICATION_JSON, outputMessage);
    }

}

注意:上面問題回答針對的只是OAuth2AccessToken,所以實際上,如果我們的項目還有其他的http請求需要處理,則需要修改上面的轉換器,只處理token的回執保持兼容,其他請求的回執仍然保持原樣

public class OAuth2AccessTokenMessageConverter extends AbstractHttpMessageConverter<Object> { //這裏替換成Object
    
    @Override
    protected Object readInternal(Class<? extends OAuth2AccessToken> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        //需要寫自己的轉換規則
    }
    
    @Override
    protected void writeInternal(Object t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        String jsonString = JSON.toJSONString(t);
        if (t instanceof DefaultOAuth2AccessToken) {
            DefaultOAuth2AccessToken oAuth2AccessToken = JSON.parseObject(jsonString, DefaultOAuth2AccessToken.class);
            Map<String, Object> data = new HashMap<>(8);
            data.put(OAuth2AccessToken.ACCESS_TOKEN, oAuth2AccessToken.getValue());
            data.put(OAuth2AccessToken.TOKEN_TYPE, oAuth2AccessToken.getTokenType());
            data.put(OAuth2AccessToken.EXPIRES_IN, oAuth2AccessToken.getExpiresIn());
            data.put(OAuth2AccessToken.SCOPE, String.join(" ", oAuth2AccessToken.getScope()));

            //獲取refreshToken
            JSONObject jsonObject = JSON.parseObject(jsonString);
            String refreshTokenString = jsonObject.getString("refreshToken");
            if (Objects.nonNull(refreshTokenString)) {
                data.put(OAuth2AccessToken.REFRESH_TOKEN, JSONObject.parseObject(refreshTokenString).getString("value"));
            }
            jsonString = JSON.toJSONString(data);
        }
        StreamUtils.copy(jsonString, DEFAULT_CHARSET, outputMessage.getBody());
    }

}

 

加入MessageConverters

@Configuration
public class Oauth2WebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
       converters.add(0, new OAuth2AccessTokenMessageConverter());
    }
}

你可以參考OAuth2AccessToken這個序列化類OAuth2AccessTokenJackson2Serializer進行編寫相應的代碼

 

發佈了13 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章