Spring Security 5 - OAuth2ログイン

Spring Security 5 – OAuth2ログイン

1. 概要

Spring Security 5では、外部承認サーバーの構成に使用できる新しいOAuth2LoginConfigurerクラスが導入されています。

この記事では、we’ll explore some of the various configuration options available for the oauth2Login() element

2. Mavenの依存関係

標準のSpringおよびSpringSecurityの依存関係に加えて、spring-security-oauth2-clientおよびspring-security-oauth2-joseの依存関係も追加する必要があります。


    org.springframework.security
    spring-security-oauth2-client


   org.springframework.security
   spring-security-oauth2-jose

この例では、依存関係はSpring Bootスターターの親であるバージョン2.x,によって管理されています。これは、Spring Securityアーティファクトのバージョン5.xに対応しています。

3. クライアントのセットアップ

Spring Bootプロジェクトでは、設定するクライアントごとにいくつかの標準プロパティを追加するだけです。

認証プロバイダーとしてGoogleとFacebookに登録されているクライアントでログインするためのプロジェクトを設定しましょう。

3.1. クライアント資格情報の取得

Google OAuth2認証のクライアント認証情報を取得するには、Google API Console - セクション「認証情報」に進んでください。

ここでは、Webアプリケーションのタイプ「OAuth2クライアントID」の認証情報を作成します。 これにより、GoogleがクライアントIDとシークレットを設定します。

また、Googleコンソールで承認済みリダイレクトURIを構成する必要があります。これは、ユーザーがGoogleで正常にログインした後にリダイレクトされるパスです。

デフォルトでは、Spring BootはこのリダイレクトURIを/login/oauth2/code/{registrationId}.として構成します。したがって、Googleの場合は次のURIを追加します。

http://localhost:8081/login/oauth2/code/google

Facebookで認証するためのクライアント資格情報を取得するには、Facebook for Developers Webサイトにアプリケーションを登録し、対応するURIを「有効なOAuthリダイレクトURI」として設定する必要があります。

http://localhost:8081/login/oauth2/code/facebook

3.3. セキュリティ構成

次に、application.propertiesファイルにクライアント資格情報を追加する必要があります。 The Spring Security properties are prefixed with “spring.security.oauth2.client.registration” followed by the client name, then the name of the client property:

spring.security.oauth2.client.registration.google.client-id=
spring.security.oauth2.client.registration.google.client-secret=

spring.security.oauth2.client.registration.facebook.client-id=
spring.security.oauth2.client.registration.facebook.client-secret=

必要なすべてのBeanをセットアップするAdding these properties for at least one client will enable the Oauth2ClientAutoConfiguration class

自動Webセキュリティ構成は、単純なoauth2Login()要素を定義することと同じです。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
         .anyRequest().authenticated()
         .and()
         .oauth2Login();
    }
}

ここでは、oauth2Login()要素が既知のhttpBasic()およびformLogin()要素と同様の方法で使用されていることがわかります。

これで、保護されたURLにアクセスしようとすると、アプリケーションは2つのクライアントで自動生成されたログインページを表示します。

image

3.4. 他のクライアント

Note that in addition to Google and Facebook, the Spring Security project also contains default configurations for GitHub and Okta.これらのデフォルト構成は、認証に必要なすべての情報を提供します。これにより、クライアントの資格情報のみを入力できます。

Spring Securityで構成されていない別の認証プロバイダーを使用する場合は、承認URIやトークンURIなどの情報を使用して完全な構成を定義する必要があります。 Hereは、Spring Securityのデフォルト構成を調べて、必要なプロパティを把握しています。

4. 非ブートプロジェクトでのセットアップ

4.1. ClientRegistrationRepositoryBeanの作成

許可サーバーが所有するクライアント情報の内部表現を含むIf we’re not working with a Spring Boot application, we’ll need to define a ClientRegistrationRepository bean

@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private static List clients = Arrays.asList("google", "facebook");

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        List registrations = clients.stream()
          .map(c -> getRegistration(c))
          .filter(registration -> registration != null)
          .collect(Collectors.toList());

        return new InMemoryClientRegistrationRepository(registrations);
    }
}

ここでは、ClientRegistrationオブジェクトのリストを使用してInMemoryClientRegistrationRepositoryを作成しています。

4.2. ClientRegistrationオブジェクトの構築

これらのオブジェクトを構築するgetRegistration()メソッドを見てみましょう。

private static String CLIENT_PROPERTY_KEY
  = "spring.security.oauth2.client.registration.";

@Autowired
private Environment env;

private ClientRegistration getRegistration(String client) {
    String clientId = env.getProperty(
      CLIENT_PROPERTY_KEY + client + ".client-id");

    if (clientId == null) {
        return null;
    }

    String clientSecret = env.getProperty(
      CLIENT_PROPERTY_KEY + client + ".client-secret");

    if (client.equals("google")) {
        return CommonOAuth2Provider.GOOGLE.getBuilder(client)
          .clientId(clientId).clientSecret(clientSecret).build();
    }
    if (client.equals("facebook")) {
        return CommonOAuth2Provider.FACEBOOK.getBuilder(client)
          .clientId(clientId).clientSecret(clientSecret).build();
    }
    return null;
}

ここでは、同様のapplication.propertiesファイルからクライアントの認証情報を読み取り、GoogleおよびFacebookクライアントの残りのクライアントプロパティにSpring Securityですでに定義されているCommonOauth2Provider列挙型を使用しています。

ClientRegistrationインスタンスはクライアントに対応します。

4.3. ClientRegistrationRepositoryの登録

最後に、ClientRegistrationRepository Beanに基づいてOAuth2AuthorizedClientService Beanを作成し、両方をoauth2Login()要素に登録する必要があります。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().authenticated()
      .and()
      .oauth2Login()
      .clientRegistrationRepository(clientRegistrationRepository())
      .authorizedClientService(authorizedClientService());
}

@Bean
public OAuth2AuthorizedClientService authorizedClientService() {

    return new InMemoryOAuth2AuthorizedClientService(
      clientRegistrationRepository());
}

ここで証明されているように、we can use the clientRegistrationRepository() method of oauth2Login() to register a custom registration repository.

また、カスタムログインページは自動的に生成されなくなるため、定義する必要があります。 これについては、次のセクションで詳しく説明します。

ログインプロセスのさらなるカスタマイズを続けましょう。

5. oauth2Login()のカスタマイズ

OAuth 2プロセスが使用する要素がいくつかあり、oauth2Login()メソッドを使用してカスタマイズできます。

これらの要素はすべてSpringBootのデフォルト構成であり、明示的な構成は必要ありません。

構成でこれらをカスタマイズする方法を見てみましょう。

5.1. カスタムログインページ

Spring Bootはデフォルトのログインページを生成しますが、通常は独自のカスタマイズされたページを定義する必要があります。

を使用して、oauth2Login()要素の新しいログインURLを構成することから始めましょう。 loginPage()メソッド:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
      .antMatchers("/oauth_login")
      .permitAll()
      .anyRequest()
      .authenticated()
      .and()
      .oauth2Login()
      .loginPage("/oauth_login");
}

ここでは、ログインURLを/oauth_login.に設定しました

次に、このURLにマップするメソッドを使用してLoginControllerを定義しましょう。

@Controller
public class LoginController {

    private static String authorizationRequestBaseUri
      = "oauth2/authorization";
    Map oauth2AuthenticationUrls
      = new HashMap<>();

    @Autowired
    private ClientRegistrationRepository clientRegistrationRepository;

    @GetMapping("/oauth_login")
    public String getLoginPage(Model model) {
        // ...

        return "oauth_login";
    }
}

ClientRegistrationRepository Beanから取得するThis method has to send a map of the clients available and their authorization endpoints to the view

public String getLoginPage(Model model) {
    Iterable clientRegistrations = null;
    ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository)
      .as(Iterable.class);
    if (type != ResolvableType.NONE &&
      ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
        clientRegistrations = (Iterable) clientRegistrationRepository;
    }

    clientRegistrations.forEach(registration ->
      oauth2AuthenticationUrls.put(registration.getClientName(),
      authorizationRequestBaseUri + "/" + registration.getRegistrationId()));
    model.addAttribute("urls", oauth2AuthenticationUrls);

    return "oauth_login";
}

最後に、oauth_login.htmlページを定義する必要があります。

Login with:

Client

これは、各クライアントで認証するためのリンクを表示する単純なHTMLページです。

いくつかのスタイリングを追加した後、より見栄えの良いログインページを作成できます。

image

5.2. カスタム認証の成功と失敗の動作

さまざまな方法を使用して、認証後の動作を制御できます。

  • defaultSuccessUrl()およびfailureUrl() –ユーザーを特定のURLにリダイレクトします

  • successHandler()およびfailureHandler() –認証プロセスに続いてカスタムロジックを実行します

ユーザーをリダイレクトするカスタムURLを設定する方法を見てみましょう。

.oauth2Login()
  .defaultSuccessUrl("/loginSuccess")
  .failureUrl("/loginFailure");

ユーザーが認証前に保護されたページにアクセスした場合、ログイン後にそのページにリダイレクトされます。それ以外の場合は、/loginSuccess.にリダイレクトされます

ユーザーが以前に保護されたページにいたかどうかに関係なく、ユーザーが常に/loginSuccess URLに送信されるようにする場合は、メソッドdefaultSuccessUrl(“/loginSuccess”, true)を使用できます。

カスタムハンドラーを使用するには、AuthenticationSuccessHandlerまたはAuthenticationFailureHandlerインターフェースを実装するクラスを作成し、継承されたメソッドをオーバーライドしてから、successHandler()メソッドとfailureHandler()メソッドを使用してBeanを設定する必要があります。 。

5.3. カスタム認証エンドポイント

承認エンドポイントは、Spring Securityが外部サーバーへの承認リクエストをトリガーするために使用するエンドポイントです。

まず、let’s set new properties for the authorization endpoint

.oauth2Login()
  .authorizationEndpoint()
  .baseUri("/oauth2/authorize-client")
  .authorizationRequestRepository(authorizationRequestRepository());

ここでは、baseUriをデフォルトの/oauth2/authorization.ではなく/oauth2/authorize-clientに変更しました。また、定義する必要のあるauthorizationRequestRepository()Beanを明示的に設定しています。

@Bean
public AuthorizationRequestRepository
  authorizationRequestRepository() {

    return new HttpSessionOAuth2AuthorizationRequestRepository();
}

この例では、BeanにSpringが提供する実装を使用しましたが、カスタムの実装を提供することもできます。

5.4. カスタムトークンエンドポイント

トークンendpointはアクセストークンを処理します。

デフォルトの応答クライアント実装のLet’s explicitly configure the tokenEndpoint()

.oauth2Login()
  .tokenEndpoint()
  .accessTokenResponseClient(accessTokenResponseClient());

そして、これが応答クライアントBeanです。

@Bean
public OAuth2AccessTokenResponseClient
  accessTokenResponseClient() {

    return new NimbusAuthorizationCodeTokenResponseClient();
}

この構成はデフォルトの構成と同じであり、プロバイダーと認証コードを交換することに基づいたSpring実装を使用しています。

もちろん、カスタムレスポンスクライアントを代用することもできます。

5.5. カスタムリダイレクトエンドポイント

これは、外部プロバイダーとの認証後にリダイレクトするエンドポイントです。

リダイレクトエンドポイントのbaseUriを変更する方法を見てみましょう。

.oauth2Login()
  .redirectionEndpoint()
  .baseUri("/oauth2/redirect")

デフォルトのURIはlogin/oauth2/code.です

これを変更する場合は、各ClientRegistrationredirectUriTemplateプロパティも更新し、新しいURIを各クライアントの承認済みリダイレクトURIとして追加する必要があることに注意してください。

5.6. カスタムユーザー情報エンドポイント

ユーザー情報エンドポイントは、ユーザー情報を取得するために活用できる場所です。

We can customize this endpoint using the userInfoEndpoint() method。 このために、userService()customUserType()などのメソッドを使用して、ユーザー情報の取得方法を変更できます。

6. ユーザー情報へのアクセス

達成したい一般的なタスクは、ログインしているユーザーに関する情報を見つけることです。 このため、we can make a request to the user information endpoint.

まず、現在のユーザートークンに対応するクライアントを取得する必要があります。

@Autowired
private OAuth2AuthorizedClientService authorizedClientService;

@GetMapping("/loginSuccess")
public String getLoginInfo(Model model, OAuth2AuthenticationToken authentication) {
    OAuth2AuthorizedClient client = authorizedClientService
      .loadAuthorizedClient(
        authentication.getAuthorizedClientRegistrationId(),
          authentication.getName());
    //...
    return "loginSuccess";
}

次に、クライアントのユーザー情報エンドポイントにリクエストを送信し、userAttributes Map:を取得します

String userInfoEndpointUri = client.getClientRegistration()
  .getProviderDetails().getUserInfoEndpoint().getUri();

if (!StringUtils.isEmpty(userInfoEndpointUri)) {
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + client.getAccessToken()
      .getTokenValue());
    HttpEntity entity = new HttpEntity("", headers);
    ResponseEntity response = restTemplate
      .exchange(userInfoEndpointUri, HttpMethod.GET, entity, Map.class);
    Map userAttributes = response.getBody();
    model.addAttribute("name", userAttributes.get("name"));
}

nameプロパティをModel属性として追加することにより、ユーザーへのウェルカムメッセージとしてloginSuccessビューに表示できます。

image

name,の他に、userAttributes Mapにはemail, family_name,picture, locale.などのプロパティも含まれています

7. 結論

この記事では、Spring Securityのoauth2Login()要素を使用して、GoogleやFacebookなどのさまざまなプロバイダーで認証する方法を説明しました。 また、このプロセスをカスタマイズするためのいくつかの一般的なシナリオも実行しました。

例の完全なソースコードはover on GitHubにあります。