Spring Security 5.1クライアントでの承認とトークン要求のカスタマイズ

1概要

OAuth 2 APIが標準とは少し異なる場合があります。その場合は、標準のOAuth 2要求に対していくつかのカスタマイズを行う必要があります。

Spring Security 5.1はOAuth2認証とトークンリクエストをカスタマイズするためのサポートを提供します。

このチュートリアルでは、リクエストパラメータとレスポンス処理をカスタマイズする方法を説明します。

2カスタム認証リクエスト

まず、OAuth2認証リクエストをカスタマイズします。必要に応じて、標準パラメータを変更したり、認証要求に追加のパラメータを追加したりできます。

そのためには、 自分の OAuth2AuthorizationRequestResolver : を実装する必要があります。

public class CustomAuthorizationRequestResolver
  implements OAuth2AuthorizationRequestResolver {

    private OAuth2AuthorizationRequestResolver defaultResolver;

    public CustomAuthorizationRequestResolver(
      ClientRegistrationRepository repo, String authorizationRequestBaseUri) {
        defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repo, authorizationRequestBaseUri);
    }

   //...
}

基本機能を提供するために DefaultOAuth2AuthorizationRequestResolver を使用したことに注意してください。

また、カスタマイズロジックを追加するために resolve() メソッドをオーバーライドします。

public class CustomAuthorizationRequestResolver
  implements OAuth2AuthorizationRequestResolver {

   //...

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
        OAuth2AuthorizationRequest req = defaultResolver.resolve(request);
        if(req != null) {
            req = customizeAuthorizationRequest(req);
        }
        return req;
    }

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
        OAuth2AuthorizationRequest req = defaultResolver.resolve(request, clientRegistrationId);
        if(req != null) {
            req = customizeAuthorizationRequest(req);
        }
        return req;
    }

    private OAuth2AuthorizationRequest customizeAuthorizationRequest(
      OAuth2AuthorizationRequest req) {
       //...
    }

}

次のセクションで説明するように、 customizeAuthorizationRequest() メソッドを使用して後でカスタマイズを追加します。

カスタムの OAuth2AuthorizationRequestResolver を実装したら、それをセキュリティ設定に追加する必要があります。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.oauth2Login()
          .authorizationEndpoint()
          .authorizationRequestResolver(
            new CustomAuthorizationRequestResolver(
              clientRegistrationRepository(), "/oauth2/authorize-client"))
       //...
    }
}
  • ここでは、 oauth2Login()。authorizationEndpoint()。authorizationRequestResolver() を使用して、カスタムの__OAuth2AuthorizationRequestResolverを注入しました。

3承認依頼標準パラメータのカスタマイズ

それでは、実際のカスタマイズについて説明しましょう。必要に応じて OAuth2AuthorizationRequest を変更できます。

初心者のために、** それぞれの認証リクエストに対して標準のパラメータを変更できます。

たとえば、独自の "state" パラメータを生成できます。

private OAuth2AuthorizationRequest customizeAuthorizationRequest(
  OAuth2AuthorizationRequest req) {
    return OAuth2AuthorizationRequest
      .from(req).state("xyz").build();
}

4認証要求追加パラメータ

  • OAuth2AuthorizationRequest additionalParameters() メソッドを使用し、 Mapを渡して AAuth2AuthorizationRequest__に追加のパラメータを追加することもできます。

private OAuth2AuthorizationRequest customizeAuthorizationRequest(
  OAuth2AuthorizationRequest req) {
    Map<String,Object> extraParams = new HashMap<String,Object>();
    extraParams.putAll(req.getAdditionalParameters());
    extraParams.put("test", "extra");

    return OAuth2AuthorizationRequest
      .from(req)
      .additionalParameters(extraParams)
      .build();
}

また、新しいパラメータを追加する前に、古い__付加パラメータを含める必要があります。

Okta Authorization Serverで使用される認証リクエストをカスタマイズして、より実用的な例を見てみましょう。

4.1. カスタムOkta認証リクエスト

Okta には、より多くの機能をユーザーに提供するための認証要求用の追加のオプションパラメータがあります。たとえば、 idp はアイデンティティプロバイダを示します。

アイデンティティプロバイダはデフォルトでOktaですが、 idp パラメータを使用してカスタマイズできます。

private OAuth2AuthorizationRequest customizeOktaReq(OAuth2AuthorizationRequest req) {
    Map<String,Object> extraParams = new HashMap<String,Object>();
    extraParams.putAll(req.getAdditionalParameters());
    extraParams.put("idp", "https://idprovider.com");
    return OAuth2AuthorizationRequest
      .from(req)
      .additionalParameters(extraParams)
      .build();
}

5カスタムトークンリクエスト

これで、OAuth2トークンリクエストをカスタマイズする方法がわかりました。

  • OAuth2AccessTokenResponseClient をカスタマイズすることでトークンリクエストをカスタマイズできます。

OAuth2AccessTokenResponseClient のデフォルト実装は DefaultAuthorizationCodeTokenResponseClient です。

カスタムの RequestEntityConverter を提供することでトークン要求自体をカスタマイズできます。また、 DefaultAuthorizationCodeTokenResponseClient RestOperations をカスタマイズすることでトークン応答処理をカスタマイズすることもできます。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.tokenEndpoint()
          .accessTokenResponseClient(accessTokenResponseClient())
           //...
    }

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient(){
        DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
          new DefaultAuthorizationCodeTokenResponseClient();
        accessTokenResponseClient.setRequestEntityConverter(new CustomRequestEntityConverter());

        OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter =
          new OAuth2AccessTokenResponseHttpMessageConverter();
        tokenResponseHttpMessageConverter.setTokenResponseConverter(new CustomTokenResponseConverter());
        RestTemplate restTemplate = new RestTemplate(Arrays.asList(
          new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

        accessTokenResponseClient.setRestOperations(restTemplate);
        return accessTokenResponseClient;
    }
}
  • tokenEndpoint()を使用して独自の __OAuth2AccessTokenResponseClientをインジェクトすることができます。

トークン要求パラメータをカスタマイズするには、 CustomRequestEntityConverter. を実装します。同様に、トークン応答の処理をカスタマイズするには、 CustomTokenResponseConverter. を実装します。

次のセクションでは、 CustomRequestEntityConverter CustomTokenResponseConverter の両方について説明します。

6. トークン要求追加パラメータ

これで、カスタムの Converter を構築することによって、トークン要求に追加のパラメータを追加する方法がわかります。

public class CustomRequestEntityConverter implements
  Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> {

    private OAuth2AuthorizationCodeGrantRequestEntityConverter defaultConverter;

    public CustomRequestEntityConverter() {
        defaultConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter();
    }

    @Override
    public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest req) {
        RequestEntity<?> entity = defaultConverter.convert(req);
        MultiValueMap<String, String> params = (MultiValueMap<String,String>) entity.getBody();
        params.add("test2", "extra2");
        return new RequestEntity<>(params, entity.getHeaders(),
          entity.getMethod(), entity.getUrl());
    }

}
  • Converter OAuth2AuthorizationCodeGrantRequest RequestEntityに変換します。 **

既定のコンバーター OAuth2AuthorizationCodeGrantRequestEntityConverter を使用して基本機能を提供し、 RequestEntity 本体に追加のパラメーターを追加しました。

7. カスタムトークンレスポンス処理

これで、トークンレスポンスの処理をカスタマイズします。

デフォルトのトークンレスポンスコンバータhttps://github.com/spring-projects/spring-security/blob/master/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/を使用できます。開始点としてhttp/converter/OAuth2AccessTokenResponseHttpMessageConverter.java#L134[ OAuth2AccessTokenResponseHttpMessageConverter ]。

  • "scope" パラメータを異なる方法で処理するために CustomTokenResponseConverter__を実装します。**

public class CustomTokenResponseConverter implements
  Converter<Map<String, String>, OAuth2AccessTokenResponse> {
    private static final Set<String> TOKEN__RESPONSE__PARAMETER__NAMES = Stream.of(
        OAuth2ParameterNames.ACCESS__TOKEN,
        OAuth2ParameterNames.TOKEN__TYPE,
        OAuth2ParameterNames.EXPIRES__IN,
        OAuth2ParameterNames.REFRESH__TOKEN,
        OAuth2ParameterNames.SCOPE).collect(Collectors.toSet());

    @Override
    public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS__TOKEN);

        Set<String> scopes = Collections.emptySet();
        if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
            String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
            scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, ","))
                .collect(Collectors.toSet());
        }

       //...
        return OAuth2AccessTokenResponse.withToken(accessToken)
          .tokenType(accessTokenType)
          .expiresIn(expiresIn)
          .scopes(scopes)
          .refreshToken(refreshToken)
          .additionalParameters(additionalParameters)
          .build();
    }

}

トークン応答コンバーターは、 Map を__OAuth2AccessTokenResponseに変換します。

この例では、 "scope" パラメータを、スペースで区切られた__Stringの代わりにカンマで区切られたものとして解析しました。

LinkedInを認証サーバーとして使用してトークン応答をカスタマイズすることによる別の実用的な例を見てみましょう。

7.1. LinkedInトークンの応答処理

最後に、https://developer.linkedin.com/docs/oauth2[LinkedIn]トークン応答の処理方法を見てみましょう。

これには access token expires__inしか含まれていません。

私たちは単に私たち自身のトークン応答コンバータを実装して手動で token type__を設定することができます。

public class LinkedinTokenResponseConverter
  implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {

    @Override
    public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS__TOKEN);
        long expiresIn = Long.valueOf(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES__IN));

        OAuth2AccessToken.TokenType accessTokenType = OAuth2AccessToken.TokenType.BEARER;

        return OAuth2AccessTokenResponse.withToken(accessToken)
          .tokenType(accessTokenType)
          .expiresIn(expiresIn)
          .build();
    }
}

8結論

この記事では、リクエストパラメータを追加または変更することによって、OAuth2認証とトークンリクエストをカスタマイズする方法を学びました。

例の完全なソースコードは、https://github.com/eugenp/tutorials/tree/master/spring-5-security-oauth[GitHubで利用可能]です。

前の投稿:Javaストリーム
次の投稿:春にRestTemplateをモックする