一文帶你了解 OAuth2 協(xié)議與 Spring Security OAuth2 集成!

OAuth 2.0 允許第三方應(yīng)用程序訪問受限的HTTP資源的授權(quán)協(xié)議,像平常大家使用Github、Google賬號來登陸其他系統(tǒng)時使用的就是 OAuth 2.0 授權(quán)框架,下圖就是使用Github賬號登陸Coding系統(tǒng)的授權(quán)頁面圖:

類似使用 OAuth 2.0 授權(quán)的還有很多,本文將介紹 OAuth 2.0 相關(guān)的概念如:角色、授權(quán)類型等知識,以下是我整理一張 OAuth 2.0 授權(quán)的腦頭,希望對大家了解 OAuth 2.0 授權(quán)協(xié)議有幫助。

文章將以腦圖中的內(nèi)容展開 OAuth 2.0 協(xié)議同時除了 OAuth 2.0 外,還會配合 Spring Security OAuth2 來搭建OAuth2客戶端,這也是學(xué)習(xí) OAuth 2.0 的目的,直接應(yīng)用到實際項目中,加深對 OAuth 2.0 和 Spring Security 的理解。

OAuth 2.0 角色

OAuth 2.0 中有四種類型的角色分別為:資源Owner、授權(quán)服務(wù)、客戶端、資源服務(wù),這四個角色負(fù)責(zé)不同的工作,為了方便理解先給出一張大概的流程圖,細(xì)節(jié)部分后面再分別展開:

OAuth 2.0 大概授權(quán)流程

資源 Owner

資源 Owner可以理解為一個用戶,如之前提到使用Github登陸Coding中的例子中,用戶使用GitHub賬號登陸Coding,Coding就需要知道用戶在GitHub系統(tǒng)中的的頭像、用戶名、email等信息,這些賬戶信息都是屬于用戶的這樣就不難理解資源 Owner了。在Coding請求從GitHub中獲取想要的用戶信息時也是沒那容易的,GitHub為了安全起見,至少要通過用戶(資源 Owner)的同意才行。

資源服務(wù)器

明白資源 Owner后,相信你已經(jīng)知道什么是資源服務(wù)器,在這個例子中用戶賬號的信息都存放在GitHub的服務(wù)器中,所以這里的資源服務(wù)器就是GitHub服務(wù)器。GitHub服務(wù)器負(fù)責(zé)保存、保護(hù)用戶的資源,任何其他第三方系統(tǒng)想到使用這些信息的系統(tǒng)都需要經(jīng)過資源 Owner授權(quán),同時依照 OAuth 2.0 授權(quán)流程進(jìn)行交互。

客戶端

知道資源 Owner資源服務(wù)器后,OAuth中的客戶端角色也相對容易理解了,簡單的說客戶端就是想要獲取資源的系統(tǒng),如例子中的使用GitHub登陸Coding時,Coding就是OAuth中的客戶端??蛻舳酥饕?fù)責(zé)發(fā)起授權(quán)請求、獲取AccessToken、獲取用戶資源。

授權(quán)服務(wù)器

有了資源 Owner、資源服務(wù)器客戶端還不能完成OAuth授權(quán)的,還需要有授權(quán)服務(wù)器。在OAuth中授權(quán)服務(wù)器除了負(fù)責(zé)與用戶(資源 Owner)、客房端(Coding)交互外,還要生成AccessToken、驗證AccessToken等功能,它是OAuth授權(quán)中的非常重要的一環(huán),在例子中授權(quán)服務(wù)器就是GitHub的服務(wù)器。

小結(jié)

OAuth中:資源Owner授權(quán)服務(wù)客戶端、資源服務(wù)有四個角色在使用GitHub登陸Coding的例子中分別表示:

  • 資源Owner:GitHub用戶
  • 授權(quán)服務(wù):GitHub服務(wù)器
  • 客戶端:Coding系統(tǒng)
  • 資源服務(wù):GitHub服務(wù)器

其中授權(quán)服務(wù)服務(wù)器、資源服務(wù)器可以單獨搭建(鬼知道GitHub怎么搭建的)。在微服務(wù)器架構(gòu)中可單獨弄一個授權(quán)服務(wù),資源服務(wù)服務(wù)可以多個如:用戶資源、倉庫資源等,可根據(jù)需求自由分服務(wù)。

OAuth2 Endpoint

OAuth2有三個重要的Endpoint其中授權(quán) Endpoint、Token Endpoint結(jié)點在授權(quán)服務(wù)器中,還有一個可選的重定向 Endpoint在客戶端中。

  • 授權(quán) Endpoint:使用授權(quán) Endpoint去獲取資源Owner的授權(quán)
  • Token Endpoint:客戶端獲取token
  • 重定向 Endpoint:授權(quán)服務(wù)器使用重定向 Endpoint返回授權(quán)響應(yīng)給客戶端

授權(quán)類型

通過四個OAuth角色,應(yīng)該對OAuth協(xié)議有一個大概的認(rèn)識,不過可能還是一頭霧水不知道OAuth中的角色是如何交互的,沒關(guān)系繼續(xù)往下看一下授權(quán)類型就知道OAuth中的角色是如何完成自己的職責(zé),進(jìn)一步對OAuth的理解。在OAuth中定義了四種授權(quán)類型,分別為:

  • 授權(quán)碼授權(quán)
  • 客房端憑證授權(quán)
  • 資源Owner的密碼授權(quán)
  • 隱式的授權(quán)

不同的授權(quán)類型可以使用在不同的場景中。

授權(quán)碼授權(quán)

這種形式就是我們常見的授權(quán)形式(如使用GitHub賬號登陸Coding),在整個授權(quán)流程中會有資源Owner、授權(quán)服務(wù)器客戶端三個OAuth角色參與,之所以叫做授權(quán)碼授權(quán)是因為在交互流程中授權(quán)服務(wù)器會給客房端發(fā)放一個code,隨后客房端拿著授權(quán)服務(wù)器發(fā)放的code繼續(xù)進(jìn)行授權(quán)如:請求授權(quán)服務(wù)器發(fā)放AccessToken。

為方便理解再將上圖的內(nèi)容帶進(jìn)真實的場景中,用文字表述一下整個流程:

  • A.1、用戶訪問Coding登陸頁(https://coding.net/login),點擊Github登陸按鈕;
  • A.2、Coding服務(wù)器將瀏覽器重定向到Github的授權(quán)頁(https://github.com/login/oauth/authorize?client_id=a5ce5a6c7e8c39567ca0&scope=user:email&redirect_uri=https://coding.net/api/oauth/github/callback&response_type=code),同時URL帶上client_idredirect_uri參數(shù);
  • B.1、用戶輸入用戶名、密碼登陸Github;
  • B.2、用戶點擊授權(quán)按鈕,同意授權(quán);
  • C.1、Github授權(quán)服務(wù)器返回code
  • C.2、Github通過將瀏覽器重定向到A.2步驟中傳遞的redirect_uri地址(https://coding.net/api/oauth/github/callback&response_type=code);
  • D、Coding拿到code后,調(diào)用Github授權(quán)服務(wù)器API獲取AccessToken,由于這一步是在Coding服務(wù)器后臺做的瀏覽器中捕獲不到,基本就是使用code訪問github的access_token節(jié)點獲取AccessToken;

以上是大致的授權(quán)碼授權(quán)流程,大部分是客戶端與授權(quán)服務(wù)器的交互,整個過程中有幾個參數(shù)說明如下:

  • client_id:在Github中注冊的Appid,用于標(biāo)記客戶端
  • redirect_uri:可以理解一個callback,授權(quán)服務(wù)器驗證完客戶端與用戶名等信息后將瀏覽器重定向到此地址并帶上code參數(shù)
  • code:由授權(quán)服務(wù)器返回的一個憑證,用于獲取AccessToken
  • state:由客戶端傳遞給授權(quán)服務(wù)器,授權(quán)服務(wù)器一般到調(diào)用redirect_uri時原樣返回
授權(quán)碼授權(quán)請求

在使用授權(quán)碼授權(quán)的模式中,作為客戶端請求授權(quán)的的時候都需要按規(guī)范請求,以下是使用授權(quán)碼授權(quán)發(fā)起授權(quán)時所需要的參數(shù) :

在這里插入圖片描述

如使用Github登陸Coding例子中的https://github.com/login/oauth/authorize?client_id=a5ce5a6c7e8c39567ca0&scope=user:email&redirect_uri=https://coding.net/api/oauth/github/callback&response_type=code授權(quán)請求URL,就有client_id、redirect_uri參數(shù),至于為啥沒有response_type在下猜想是因為Github給省了吧。

授權(quán)碼授權(quán)響應(yīng)

如果用戶同意授權(quán),那授權(quán)服務(wù)器也會返回標(biāo)準(zhǔn)的OAuth授權(quán)響應(yīng):

在這里插入圖片描述

如Coding登陸中的https://coding.net/api/oauth/github/callback&response_type=code,用戶同意授權(quán)后Github授權(quán)服務(wù)器回調(diào)Coding的回調(diào)地址,同時返回code、state參數(shù)。

客戶端憑證授權(quán)

客房端憑證授權(quán)授權(quán)的過程中只會涉及客戶端與授權(quán)服務(wù)器交互,相比較其他三種授權(quán)類型是比較簡單的。一般這種授權(quán)模式是用于服務(wù)之間授權(quán),如在AWS中兩臺服務(wù)器分別為應(yīng)用服務(wù)器(A)和數(shù)據(jù)服務(wù)器(B),A 服務(wù)器需要訪問 B 服務(wù)器就需要通過授權(quán)服務(wù)器授權(quán),然后才能去訪問 B 服務(wù)器獲取數(shù)據(jù)。

簡單二步就可以完成客房端憑證授權(quán)啦,不過在使用客房端憑證授權(quán)時客戶端是直接訪問的授權(quán)服務(wù)器中獲取AccessToken接口。

客戶端憑證授權(quán)請求

客房端憑證授權(quán)中客戶端會直接發(fā)起獲取AccessToken請求授權(quán)服務(wù)器的AccessTokenEndpoint,請求參數(shù)如下:

在這里插入圖片描述

注意: 在OAuth中AccessTokenEndpoint是使用HTTP Basic認(rèn)證,在請求時還需要攜帶Authorization請求頭,如使用postman測試請求時:

其中的usernamepassword參數(shù)對于OAuth協(xié)議中的client_idclient_secretclient_idclient_secret都是由授權(quán)服務(wù)器生成的。

客戶端憑證授權(quán)響應(yīng)

授權(quán)服務(wù)器驗證完client_idclient_secret后返回token:


 {
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "token_type":"example",
   "expires_in":3600,
   "example_parameter":"example_value"
 }

用戶憑證授權(quán)

用戶憑證授權(quán)客戶端憑證授權(quán)類似,不同的地方是進(jìn)行授權(quán)時要提供用戶名和用戶的密碼。

基本流程如下:

  • A、客戶端首先需要知道用戶的憑證
  • B、使用用戶憑證獲取AccessToken
  • C、授權(quán)服務(wù)器驗證客戶端與用戶憑證,返回AccessToken
用戶憑證授權(quán)請求

用戶憑證授權(quán)請求參數(shù)要比客戶端憑證授權(quán)多usernamepwssword參數(shù):


注意: 獲取Token時使用HTTP Basic認(rèn)證,與客戶端憑證授權(quán)一樣。

用戶憑證授權(quán)響應(yīng)

用戶憑證授權(quán)響應(yīng)與客戶端憑證授權(quán)差不多:

   {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }

隱式授權(quán)

隱式授權(quán)用于獲取AccessToken,但是獲取的方式與用戶憑證授權(quán)客戶端授權(quán)不同的是,它是在訪問授權(quán)Endpoint的時候就會獲取AccessToken而不是訪問Token Endpoing,而且AccessToken的會作為redirect_uri的Segment返回。

  • A.1、A.2、瀏覽器訪問支持隱式授權(quán)的服務(wù)器的授權(quán)Endpoint;
  • B.1、用戶輸入賬號密碼;
  • B.2、用戶點擊授權(quán)按鈕,同意授權(quán);
  • C、授權(quán)服務(wù)器使用redirect_uri返回AccessToken;
  • D、授權(quán)服務(wù)器將瀏覽器重定向到redirect_uri,并攜帶AccessToken如:http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600
  • D、redirect_uri的地址是指向一個Web資源客戶端
  • E、Web資源客戶端返回一段腳本
  • F、瀏覽器執(zhí)行腳本
  • D、客戶端獲得AccessToken

隱式授權(quán)不太好理解,但是仔細(xì)比較客戶端憑證授權(quán)用戶憑證授權(quán)會發(fā)現(xiàn)隱式授權(quán)不需要知道用戶憑證客戶端憑證,這樣做相對更安全。

隱式授權(quán)請求

再使用隱式授權(quán)時,所需要請求參數(shù)如下:

在這里插入圖片描述
隱式授權(quán)響應(yīng)

隱式授權(quán)響應(yīng)參數(shù)是通過redirect_uri回調(diào)返回的,如http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA &state=xyz&token_type=example&expires_in=3600就是隱式授權(quán)響應(yīng)參數(shù),其中需要注意的是響應(yīng)的參數(shù)是使用Segment的形式的,而不是普通的URL參數(shù)。

在這里插入圖片描述

OAuth2 客戶端

前面提到過OAuth協(xié)議中有四個角色,這一節(jié)使用Spring Boot實現(xiàn)一個登陸GitHubOAuthClient,要使用OAuth2協(xié)議登陸GitHub首先要云GitHub里面申請:

申請 OAuth App

OAuth Apps

填寫必需的信息

在這里插入圖片描述

上圖中的Authorization callback URL就是redirect_uri用戶同意授權(quán)后GitHub會將瀏覽器重定向到該地址,因此先要在本地的OAuth客戶端服務(wù)中添加一個接口響應(yīng)GitHub的重定向請求。

配置OAuthClient

熟悉OAuth2協(xié)議后,我們在使用 Spring Security OAuth2 配置一個GitHub授權(quán)客戶端,使用認(rèn)證碼授權(quán)流程(可以先去看一遍認(rèn)證碼授權(quán)流程圖),示例工程依賴:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

Spring Security OAuth2 默認(rèn)集成了Github、Goolge等常用的授權(quán)服務(wù)器,因為這些常用的授權(quán)服務(wù)的配置信息都是公開的,Spring Security OAuth2 已經(jīng)幫我們配置了,開發(fā)都只需要指定必需的信息就行如:clientId、clientSecret。

Spring Security OAuth2使用Registration作為客戶端的的配置實體:

public static class Registration {
    //授權(quán)服務(wù)器提供者名稱
    private String provider;
    //客戶端id
    private String clientId;
    //客戶端憑證
    private String clientSecret;
      ....

下面是之前注冊好的 GitHub OAuth App 的信息:

spring.security.oauth2.client.registration.github.clientId=5fefca2daccf85bede32
spring.security.oauth2.client.registration.github.clientSecret=01dde7a7239bd18bd8a83de67f99dde864fb6524``

配置redirect_uri

Spring Security OAuth2內(nèi)置了一個redirect_uri模板:{baseUrl}/login/oauth2/code/{registrationId},其中的registrationId是在從配置中提取出來的:

spring.security.oauth2.client.registration.[registrationId].clientId=xxxxx

如在上面的GitHub客戶端的配置中,因為指定的registrationIdgithub,所以重定向uri地址就是:

{baseUrl}/login/oauth2/code/github

啟動服務(wù)器

OAuth2客戶端和重定向Uri配置好后,將服務(wù)器啟動,然后打開瀏覽器進(jìn)入:http://localhost:8080/。第一次打開因為沒有認(rèn)證會將瀏覽器重客向到GitHub的授權(quán)Endpoint

在這里插入圖片描述

常用授權(quán)服務(wù)器(CommonOAuth2Provider)

Spring Security OAuth2內(nèi)置了一些常用的授權(quán)服務(wù)器的配置,這些配置都在CommonOAuth2Provider中:

public enum CommonOAuth2Provider {

    GOOGLE {

        @Override
        public Builder getBuilder(String registrationId) {
            ClientRegistration.Builder builder = getBuilder(registrationId,
                    ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
            builder.scope("openid", "profile", "email");
            builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
            builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
            builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs");
            builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo");
            builder.userNameAttributeName(IdTokenClaimNames.SUB);
            builder.clientName("Google");
            return builder;
        }
    },

    GITHUB {

        @Override
        public Builder getBuilder(String registrationId) {
            ClientRegistration.Builder builder = getBuilder(registrationId,
                    ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
            builder.scope("read:user");
            builder.authorizationUri("https://github.com/login/oauth/authorize");
            builder.tokenUri("https://github.com/login/oauth/access_token");
            builder.userInfoUri("https://api.github.com/user");
            builder.userNameAttributeName("id");
            builder.clientName("GitHub");
            return builder;
        }
    },

    FACEBOOK {

        @Override
        public Builder getBuilder(String registrationId) {
            ClientRegistration.Builder builder = getBuilder(registrationId,
                    ClientAuthenticationMethod.POST, DEFAULT_REDIRECT_URL);
            builder.scope("public_profile", "email");
            builder.authorizationUri("https://www.facebook.com/v2.8/dialog/oauth");
            builder.tokenUri("https://graph.facebook.com/v2.8/oauth/access_token");
            builder.userInfoUri("https://graph.facebook.com/me?fields=id,name,email");
            builder.userNameAttributeName("id");
            builder.clientName("Facebook");
            return builder;
        }
    },

    OKTA {

        @Override
        public Builder getBuilder(String registrationId) {
            ClientRegistration.Builder builder = getBuilder(registrationId,
                    ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
            builder.scope("openid", "profile", "email");
            builder.userNameAttributeName(IdTokenClaimNames.SUB);
            builder.clientName("Okta");
            return builder;
        }
    };

    private static final String DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}";
}

CommonOAuth2Provider中有四個授權(quán)服務(wù)器配置:OKTAFACEBOOK 、GITHUB 、GOOGLE。在OAuth2協(xié)議中的配置項redirect_uri、Token Endpoint、授權(quán) Endpointscope都會在這里配置:

    GITHUB {

        @Override
        public Builder getBuilder(String registrationId) {
            ClientRegistration.Builder builder = getBuilder(registrationId,
                    ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
            builder.scope("read:user");
            builder.authorizationUri("https://github.com/login/oauth/authorize");
            builder.tokenUri("https://github.com/login/oauth/access_token");
            builder.userInfoUri("https://api.github.com/user");
            builder.userNameAttributeName("id");
            builder.clientName("GitHub");
            return builder;
        }
    }

重定向Uri攔截

腦瓜子有點蒙了,感覺自己就配置了clientidclientSecret一個OAuth2客戶端就完成了,其中的一些原由還沒搞明白啊。。。,最好奇的是重定向Uri是怎么被處理的。

Spring Security OAuth2 是基于 Spring Security 的,之前看過Spring Security文章,知道它的處理原理是基于過濾器的,如果你不知道的話推薦看這篇文章:《Spring Security 架構(gòu)》。在源碼中找了一下,發(fā)現(xiàn)一個可疑的Security 過濾器:

  • OAuth2LoginAuthenticationFilter:處理OAuth2授權(quán)的過濾器

這個 Security 過濾器有個常量:

public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";

是一個匹配器,之前提到過Spring Security OAuth2中有一個默認(rèn)的redirect_uri模板:{baseUrl}/{action}/oauth2/code/{registrationId}/login/oauth2/code/*正好能與redirect_uri模板匹配成功,所以OAuth2LoginAuthenticationFilter會在用戶同意授權(quán)后執(zhí)行,它的構(gòu)造方法如下:

public OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository,
                                        OAuth2AuthorizedClientService authorizedClientService) {
    this(clientRegistrationRepository, authorizedClientService, DEFAULT_FILTER_PROCESSES_URI);
}

OAuth2LoginAuthenticationFilter 主要將授權(quán)服務(wù)器返回的code拿出來,然后通過AuthenticationManager 來認(rèn)證(獲取AccessToken),下來是移除部分代碼后的源代碼:

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
        //檢查沒code與state
        if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {
            OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
            throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
        }
        //獲取 OAuth2AuthorizationRequest 
        OAuth2AuthorizationRequest authorizationRequest =
                this.authorizationRequestRepository.removeAuthorizationRequest(request, response);
        if (authorizationRequest == null) {
            OAuth2Error oauth2Error = new OAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE);
            throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
        }
         //取出 ClientRegistration  
        String registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);
        ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
        if (clientRegistration == null) {
            OAuth2Error oauth2Error = new OAuth2Error(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE,
                    "Client Registration not found with Id: " + registrationId, null);
            throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
        }
        String redirectUri = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
                .replaceQuery(null)
                .build()
                .toUriString();
                
        //認(rèn)證、獲取AccessToken
        OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri);

        Object authenticationDetails = this.authenticationDetailsSource.buildDetails(request);
        OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(
                clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
        authenticationRequest.setDetails(authenticationDetails);

        OAuth2LoginAuthenticationToken authenticationResult =
            (OAuth2LoginAuthenticationToken) this.getAuthenticationManager().authenticate(authenticationRequest);

        ...
        return oauth2Authentication;
    }

獲取AccessToken

前面提到OAuth2LoginAuthenticationFilter是使用 AuthenticationManager 來進(jìn)行OAuth2認(rèn)證的,一般情況下在 Spring Security 中的 AuthenticationManager 都是使用的 ProviderManager 來進(jìn)行認(rèn)證的,所以對應(yīng)在 Spring Security OAuth2 中有一個 OAuth2LoginAuthenticationProvider 用于獲取AccessToken:

public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider {
    private final OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
    private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;
    private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);

    ....
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        OAuth2LoginAuthenticationToken authorizationCodeAuthentication =
            (OAuth2LoginAuthenticationToken) authentication;

        // Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
        // scope
        //      REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
        if (authorizationCodeAuthentication.getAuthorizationExchange()
            .getAuthorizationRequest().getScopes().contains("openid")) {
            // This is an OpenID Connect Authentication Request so return null
            // and let OidcAuthorizationCodeAuthenticationProvider handle it instead
            return null;
        }

        OAuth2AccessTokenResponse accessTokenResponse;
        try {
            OAuth2AuthorizationExchangeValidator.validate(
                    authorizationCodeAuthentication.getAuthorizationExchange());
                    
           //訪問GitHub TokenEndpoint獲取Token
            accessTokenResponse = this.accessTokenResponseClient.getTokenResponse(
                    new OAuth2AuthorizationCodeGrantRequest(
                            authorizationCodeAuthentication.getClientRegistration(),
                            authorizationCodeAuthentication.getAuthorizationExchange()));

        } catch (OAuth2AuthorizationException ex) {
            OAuth2Error oauth2Error = ex.getError();
            throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
        }
         ...
        return authenticationResult;
    }



    @Override
    public boolean supports(Class<?> authentication) {
        return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

參考資料

歡迎關(guān)注我的公眾號:架構(gòu)文摘,獲得獨家整理120G的免費學(xué)習(xí)資源助力你的架構(gòu)師學(xué)習(xí)之路!

公眾號后臺回復(fù)arch028獲取資料:

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容