1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Spring Boot 接入 GitHub 第三方登錄,只要兩行配置!

        共 30863字,需瀏覽 62分鐘

         ·

        2021-03-31 13:43

        點(diǎn)擊上方 java項(xiàng)目開發(fā) ,選擇 星標(biāo) 公眾號

        重磅資訊,干貨,第一時(shí)間送達(dá)


        本文地址:https://www.zyc.red/Spring/Security/OAuth2/OAuth2-Client/

        概述

        OAuth(開放授權(quán))是一個(gè)開放標(biāo)準(zhǔn),允許用戶授權(quán)第三方網(wǎng)站訪問他們存儲在另外的服務(wù)提供者上的信息,而不需要將用戶名和密碼提供給第三方網(wǎng)站或分享他們數(shù)據(jù)的所有內(nèi)容。網(wǎng)上有很多關(guān)于OAuth協(xié)議的講解,這里就不在詳細(xì)解釋OAuth相關(guān)的概念了,不了解的小伙伴可以在公號后臺回復(fù) OAuth2 獲取教程鏈接。

        Spring-Security 對 OAuth2.0的支持

        截止到本文撰寫的日期為止,Spring已經(jīng)提供了對OAuth提供的支持(spring-security-oauth),但是該工程已經(jīng)被廢棄了,因?yàn)镾pring-Security工程提供了最新的OAuth2.0支持。如果你的項(xiàng)目中使用了過期的Spring-Security-OAuth,請參考《OAuth 2.0遷移指南》,本文將對OAuth2.0中的客戶端模式進(jìn)行原理分析,結(jié)合Spring官方指南中提供了一個(gè)簡單的基于spring-boot與oauth2.0集成第三方應(yīng)用登錄的案例(spring-boot-oauth2),一步一步分析其內(nèi)部實(shí)現(xiàn)的原理。

        創(chuàng)建GitHub OAuth Apps

        在Github OAuth Apps中創(chuàng)建一個(gè)新的應(yīng)用

        這個(gè)應(yīng)用相當(dāng)于我們自己的應(yīng)用(客戶端),被注冊在Github(授權(quán)服務(wù)器)中了,如果我們應(yīng)用中的用戶有g(shù)ithub賬號的話,則可以基于oauth2來登錄我們的系統(tǒng),替代原始的用戶名密碼方式。在官方指南的例子中,使用spring-security和oauth2進(jìn)行社交登陸只需要在你的pom文件中加入以下幾個(gè)依賴即可

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

        然后在配置文件中填上剛剛注冊的應(yīng)用的clientId和clientSecret

        spring:
          security:
            oauth2:
              client:
                registration:
                  github:
                    clientId: github-client-id
                    clientSecret: github-client-secret

        緊接著就像普通的spring-security應(yīng)用一樣,繼承WebSecurityConfigurerAdapter,進(jìn)行一些簡單的配置即可

        @SpringBootApplication
        @RestController
        public class SocialApplication extends WebSecurityConfigurerAdapter {

            // ...

            @Override
            protected void configure(HttpSecurity http) throws Exception {
             // @formatter:off
                http
                    .authorizeRequests(a -> a
                        .antMatchers("/""/error""/webjars/**").permitAll()
                        .anyRequest().authenticated()
                    )
                    .exceptionHandling(e -> e
                        .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
                    )
                    .oauth2Login();
                // @formatter:on
            }
        }

        也就是說我們只需要添加maven依賴以及繼承WebSecurityConfigurerAdapter進(jìn)行一些簡單的配置,一個(gè)oauth2客戶端應(yīng)用就構(gòu)建完成了。接下來按照指南上的步驟點(diǎn)擊頁面的github登錄鏈接我們的頁面就會跳轉(zhuǎn)到github授權(quán)登錄頁,等待用戶授權(quán)完成之后瀏覽器重定向到我們的callback URL最終請求user信息端點(diǎn)即可訪問到剛剛登入的github用戶信息,整個(gè)應(yīng)用的構(gòu)建是如此的簡單,背后的原理是什么呢?接下來我們開始分析。還是和以前一樣,我們在配置文件中將security的日志級別設(shè)置為debug

        logging:
          level:
            org.springframework.security: debug

        重新啟動(dòng)應(yīng)用之后,從控制臺輸出中我們可以看到與普通spring-security應(yīng)用不同的地方在于整個(gè)過濾鏈多出了以下幾個(gè)過濾器

        OAuth2AuthorizationRequestRedirectFilter
        OAuth2LoginAuthenticationFilter

        聯(lián)想oauth2的授權(quán)碼模式以及這兩個(gè)過濾器的名字,熟悉spring-security的同學(xué)心中肯定已經(jīng)有了一點(diǎn)想法了。對沒錯(cuò),spring-security對客戶端模式的支持完全就是基于這兩個(gè)過濾器來實(shí)現(xiàn)的。現(xiàn)在我們來回想以下授權(quán)碼模式的執(zhí)行流程

        1. 用戶在客戶端頁面點(diǎn)擊三方應(yīng)用登錄按鈕(客戶端就是我們剛剛注冊的github應(yīng)用)
        2. 頁面跳轉(zhuǎn)到三方應(yīng)用注冊的授權(quán)方頁面(授權(quán)服務(wù)器即github)
        3. 用戶登入授權(quán)后,github調(diào)用我們應(yīng)用的回調(diào)地址(我們剛剛注冊github應(yīng)用時(shí)填寫的回調(diào)地址)
        4. 第三步的回調(diào)地址中g(shù)ithub會將code參數(shù)放到url中,接下來我們的客戶端就會在內(nèi)部拿這個(gè)code再次去調(diào)用github的access_token地址獲取令牌

        上面就是標(biāo)準(zhǔn)的authorization_code授權(quán)模式,OAuth2AuthorizationRequestRedirectFilter的作用就是上面步驟中的1.2步的合體,當(dāng)用戶點(diǎn)擊頁面的github授權(quán)url之后,OAuth2AuthorizationRequestRedirectFilter匹配這個(gè)請求,接著它會將我們配置文件中的clientId、scope以及構(gòu)造一個(gè)state參數(shù)(防止csrf攻擊)拼接成一個(gè)url重定向到github的授權(quán)url,OAuth2LoginAuthenticationFilter的作用則是上面3.4步驟的合體,當(dāng)用戶在github的授權(quán)頁面授權(quán)之后github調(diào)用回調(diào)地址,OAuth2LoginAuthenticationFilter匹配這個(gè)回調(diào)地址,解析回調(diào)地址后的code與state參數(shù)進(jìn)行驗(yàn)證之后內(nèi)部拿著這個(gè)code遠(yuǎn)程調(diào)用github的access_token地址,拿到access_token之后通過OAuth2UserService獲取相應(yīng)的用戶信息(內(nèi)部是拿access_token遠(yuǎn)程調(diào)用github的用戶信息端點(diǎn))最后將用戶信息構(gòu)造成Authentication被SecurityContextPersistenceFilter過濾器保存到HttpSession中。下面我們就來看一下這兩個(gè)過濾器內(nèi)部執(zhí)行的原理

        OAuth2AuthorizationRequestRedirectFilter

        public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
            
            ......省略部分代碼

         @Override
         protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
           throws ServletException, IOException 
        {

          try {
           OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
           if (authorizationRequest != null) {
            this.sendRedirectForAuthorization(request, response, authorizationRequest);
            return;
           }
          } catch (Exception failed) {
           this.unsuccessfulRedirectForAuthorization(request, response, failed);
           return;
          }
                ......省略部分代碼
        }

        通過authorizationRequestResolver解析器解析請求,解析器的默認(rèn)實(shí)現(xiàn)是DefaultOAuth2AuthorizationRequestResolver,核心解析方法如下


        // 第一步解析
        @Override
        public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
            // 通過內(nèi)部的authorizationRequestMatcher來解析當(dāng)前請求中的registrationId
            // 也就是/oauth2/authorization/github中的github
            String registrationId = this.resolveRegistrationId(request);
            String redirectUriAction = getAction(request, "login");
            return resolve(request, registrationId, redirectUriAction);
        }

        // 第二步解析
        private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) {
            if (registrationId == null) {
                return null;
            }
         // 根據(jù)傳入的registrationId找到注冊的應(yīng)用信息
            ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
            if (clientRegistration == null) {
                throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
            }

            Map<String, Object> attributes = new HashMap<>();
            attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());

            OAuth2AuthorizationRequest.Builder builder;
            // 根據(jù)不同的AuthorizationGrantType構(gòu)造不同的builder
            if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
                builder = OAuth2AuthorizationRequest.authorizationCode();
                Map<String, Object> additionalParameters = new HashMap<>();
                if (!CollectionUtils.isEmpty(clientRegistration.getScopes()) &&
                    clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
                    // 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.
                    addNonceParameters(attributes, additionalParameters);
                }
                if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
                    addPkceParameters(attributes, additionalParameters);
                }
                builder.additionalParameters(additionalParameters);
            } else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
                builder = OAuth2AuthorizationRequest.implicit();
            } else {
                throw new IllegalArgumentException("Invalid Authorization Grant Type ("  +
                                                   clientRegistration.getAuthorizationGrantType().getValue() +
                                                   ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
            }

            String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);

            OAuth2AuthorizationRequest authorizationRequest = builder
                .clientId(clientRegistration.getClientId())
                .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
                .redirectUri(redirectUriStr)
                .scopes(clientRegistration.getScopes())
                // 生成隨機(jī)state值
                .state(this.stateGenerator.generateKey())
                .attributes(attributes)
                .build();

            return authorizationRequest;
        }

        DefaultOAuth2AuthorizationRequestResolver判斷請求是否是授權(quán)請求,最終返回一個(gè)OAuth2AuthorizationRequest對象給OAuth2AuthorizationRequestRedirectFilter,如果OAuth2AuthorizationRequest不為null的話,說明當(dāng)前請求是一個(gè)授權(quán)請求,那么接下來就要拿著這個(gè)請求重定向到授權(quán)服務(wù)器的授權(quán)端點(diǎn)了,下面我們接著看OAuth2AuthorizationRequestRedirectFilter發(fā)送重定向的邏輯

        private void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,
                                                  OAuth2AuthorizationRequest authorizationRequest)
         throws IOException 
        {

            if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {
                this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
            }
            this.authorizationRedirectStrategy.sendRedirect(request, response, authorizationRequest.getAuthorizationRequestUri());
        }
        1. 如果當(dāng)前是授權(quán)碼類型的授權(quán)請求那么就需要將這個(gè)請求信息保存下來,因?yàn)榻酉聛硎跈?quán)服務(wù)器回調(diào)我們需要用到這個(gè)授權(quán)請求的參數(shù)進(jìn)行校驗(yàn)等操作(比對state),這里是通過authorizationRequestRepository保存授權(quán)請求的,默認(rèn)的保存方式是通過HttpSessionOAuth2AuthorizationRequestRepository保存在httpsession中的,具體的保存邏輯很簡單,這里就不細(xì)說了。
        2. 保存完成之后就要開始重定向到授權(quán)服務(wù)端點(diǎn)了,這里默認(rèn)的authorizationRedirectStrategy是DefaultRedirectStrategy,重定向的邏輯很簡單,通過response.sendRedirect方法使前端頁面重定向到指定的授權(quán)
        public void sendRedirect(HttpServletRequest request, HttpServletResponse response,
                                 String url)
         throws IOException 
        {
            String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
            redirectUrl = response.encodeRedirectURL(redirectUrl);

            if (logger.isDebugEnabled()) {
                logger.debug("Redirecting to '" + redirectUrl + "'");
            }

            response.sendRedirect(redirectUrl);
        }

        OAuth2AuthorizationRequestRedirectFilter處理邏輯講完了,下面我們對它處理過程做一個(gè)總結(jié)

        1. 通過內(nèi)部的OAuth2AuthorizationRequestResolver解析當(dāng)前的請求,返回一個(gè)OAuth2AuthorizationRequest對象,如果當(dāng)前請求是授權(quán)端點(diǎn)請求,那么就會返回一個(gè)構(gòu)造好的對象,包含我們的client_id、state、redirect_uri參數(shù),如果對象為null的話,那么就說明當(dāng)前請求不是授權(quán)端點(diǎn)請求。注意如果OAuth2AuthorizationRequestResolver不為null的話,OAuth2AuthorizationRequestResolver內(nèi)部會將其保存在httpsession中這樣授權(quán)服務(wù)器在調(diào)用我們的回調(diào)地址時(shí)我們就能從httpsession中取出請求將state進(jìn)行對比以防csrf攻擊。
        2. 如果第一步返回的OAuth2AuthorizationRequest對象不為null的話,接下來就會通過response.sendRedirect的方法將OAuth2AuthorizationRequest中的授權(quán)端點(diǎn)請求發(fā)送到前端的響應(yīng)頭中然后瀏覽器就會重定向到授權(quán)頁面,等待用戶授權(quán)。

        OAuth2LoginAuthenticationFilter

        public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
            @Override
         public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
           throws AuthenticationException 
        {

          MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
                // 如果請求參數(shù)中沒有state和code參數(shù),說明當(dāng)前請求是一個(gè)非法請求
          if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {
           OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
           throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
          }
          // 從httpsession中取出OAuth2AuthorizationRequestRedirectFilter中保存的授權(quán)請求,
                // 如果找不到的話說明當(dāng)前請求是非法請求
          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());
          }
          
                // 如果當(dāng)前注冊的應(yīng)用中找不到授權(quán)請求時(shí)的應(yīng)用了,那么也是一個(gè)不正確的請求
          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();
          OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri);

          Object authenticationDetails = this.authenticationDetailsSource.buildDetails(request);
          OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(
            clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
          authenticationRequest.setDetails(authenticationDetails);
          
                // 將未認(rèn)證的OAuth2LoginAuthenticationToken委托給AuthenticationManager
                // 選擇合適的AuthenticationProvider來對其進(jìn)行認(rèn)證,這里的AuthenticationProvider是
                // OAuth2LoginAuthenticationProvider
          OAuth2LoginAuthenticationToken authenticationResult =
           (OAuth2LoginAuthenticationToken) this.getAuthenticationManager().authenticate(authenticationRequest);
          
                // 將最終的認(rèn)證信息封裝成OAuth2AuthenticationToken
          OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
           authenticationResult.getPrincipal(),
           authenticationResult.getAuthorities(),
           authenticationResult.getClientRegistration().getRegistrationId());
          oauth2Authentication.setDetails(authenticationDetails);
          
                // 構(gòu)造OAuth2AuthorizedClient,將所有經(jīng)過授權(quán)的客戶端信息保存起來,默認(rèn)是通過
                // AuthenticatedPrincipalOAuth2AuthorizedClientRepository來保存的,
                // 然后就能通過其來獲取之前所有已授權(quán)的client?暫時(shí)不能確定其合適的用途
          OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
           authenticationResult.getClientRegistration(),
           oauth2Authentication.getName(),
           authenticationResult.getAccessToken(),
           authenticationResult.getRefreshToken());

          this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);

          return oauth2Authentication;
         }
        }

        OAuth2LoginAuthenticationFilter的作用很簡單,就是響應(yīng)授權(quán)服務(wù)器的回調(diào)地址,核心之處在于OAuth2LoginAuthenticationProvider對OAuth2LoginAuthenticationToken的認(rèn)證。

        OAuth2LoginAuthenticationProvider

        public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider {
            
             ...省略部分代碼
            
            @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());
           // 遠(yuǎn)程調(diào)用授權(quán)服務(wù)器的access_token端點(diǎn)獲取令牌
           accessTokenResponse = this.accessTokenResponseClient.getTokenResponse(
             new OAuth2AuthorizationCodeGrantRequest(
               authorizationCodeAuthentication.getClientRegistration(),
               authorizationCodeAuthentication.getAuthorizationExchange()));

          } catch (OAuth2AuthorizationException ex) {
           OAuth2Error oauth2Error = ex.getError();
           throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
          }
          
                 
          OAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();
          Map<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();
          
                 // 通過userService使用上一步拿到的accessToken遠(yuǎn)程調(diào)用授權(quán)服務(wù)器的用戶信息
          OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
            authorizationCodeAuthentication.getClientRegistration(), accessToken, additionalParameters));

          Collection<? extends GrantedAuthority> mappedAuthorities =
           this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
          
                 // 構(gòu)造認(rèn)證成功之后的認(rèn)證信息
          OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
           authorizationCodeAuthentication.getClientRegistration(),
           authorizationCodeAuthentication.getAuthorizationExchange(),
           oauth2User,
           mappedAuthorities,
           accessToken,
           accessTokenResponse.getRefreshToken());
          authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());

          return authenticationResult;
         }
            ...省略部分代碼
        }

        OAuth2LoginAuthenticationProvider的執(zhí)行邏輯很簡單,首先通過code獲取access_token,然后通過access_token獲取用戶信息,這和標(biāo)準(zhǔn)的oauth2授權(quán)碼模式一致。

        自動(dòng)配置

        在spring指南的例子中,我們發(fā)現(xiàn)只是配置了一個(gè)簡單oauth2Login()方法,一個(gè)完整的oauth2授權(quán)流程就構(gòu)建好了,其實(shí)這完全歸功于spring-boot的autoconfigure,我們找到spring-boot-autoconfigure.jar包中的security.oauth2.client.servlet包,可以發(fā)現(xiàn)spring-boot給我們提供了幾個(gè)自動(dòng)配置類

        OAuth2ClientAutoConfiguration
        OAuth2ClientRegistrationRepositoryConfiguration
        OAuth2WebSecurityConfiguration

        其中OAuth2ClientAutoConfiguration導(dǎo)入了OAuth2ClientRegistrationRepositoryConfiguration和OAuth2WebSecurityConfiguration的配置

        OAuth2ClientRegistrationRepositoryConfiguration

        @Configuration(proxyBeanMethods = false)
        @EnableConfigurationProperties(OAuth2ClientProperties.class)
        @Conditional(ClientsConfiguredCondition.class)
        class OAuth2ClientRegistrationRepositoryConfiguration {

            @Bean
            @ConditionalOnMissingBean(ClientRegistrationRepository.class)
            InMemoryClientRegistrationRepository clientRegistrationRepository(OAuth2ClientProperties properties) {
                List<ClientRegistration> registrations = new ArrayList<>(
                    OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
                return new InMemoryClientRegistrationRepository(registrations);
            }

        }

        OAuth2ClientRegistrationRepositoryConfiguration將我們在配置文件中注冊的client構(gòu)造成ClientRegistration然后保存到內(nèi)存之中。這里有一個(gè)隱藏的CommonOAuth2Provider類,這是一個(gè)枚舉類,里面事先定義好了幾種常用的三方登錄授權(quán)服務(wù)器的各種參數(shù)例如GOOGLE、GITHUB、FACEBOO、OKTA

        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}";

         protected final ClientRegistration.Builder getBuilder(String registrationId,
                       ClientAuthenticationMethod method, String redirectUri)
         
        {
          ClientRegistration.Builder builder = ClientRegistration.withRegistrationId(registrationId);
          builder.clientAuthenticationMethod(method);
          builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
          builder.redirectUriTemplate(redirectUri);
          return builder;
         }
            
         public abstract ClientRegistration.Builder getBuilder(String registrationId);

        }

        這就是為什么我們沒有配置github授權(quán)端點(diǎn)確能夠跳轉(zhuǎn)授權(quán)頁面的原因。

        OAuth2WebSecurityConfiguration

        OAuth2WebSecurityConfiguration配置一些web相關(guān)的類,像如何去保存和獲取已經(jīng)授權(quán)過的客戶端,以及默認(rèn)的oauth2客戶端相關(guān)的配置

        @Configuration(proxyBeanMethods = false)
        @ConditionalOnBean(ClientRegistrationRepository.class)
        class OAuth2WebSecurityConfiguration {

         @Bean
         @ConditionalOnMissingBean
         OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {
          return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
         }

         @Bean
         @ConditionalOnMissingBean
         OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {
          return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
         }
         
            // 默認(rèn)的oauth2客戶端相關(guān)的配置
         @Configuration(proxyBeanMethods = false)
         @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
         static class OAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

          @Override
          protected void configure(HttpSecurity http) throws Exception {
           http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
           http.oauth2Login(Customizer.withDefaults());
           http.oauth2Client();
          }

         }

        }
        --完--
        推薦閱讀:
        怎么接私貨?這個(gè)渠道你100%有用!請收藏!

        ,點(diǎn)個(gè)在看 
        瀏覽 47
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            我爱我的香蕉视频 | 欧美激情视频大全 | 黄片免费在线观看av | 成人毛片免费观看视频 | 肏逼毛片| 日日骚网站| 女人30分钟高潮毛片 | 2018天天弄国产大片_99久久 | 久久精品夜色国产亚洲AV | 稚女的小泬一级A片 |