Spring Security 5 - OAuth2ログイン

1概要

Spring Security 5では、外部の認証サーバを設定するために使用できる新しい OAuth2LoginConfigurer クラスが導入されました。

この記事では、** oauth2Login() 要素に使用できるさまざまな設定オプションについて説明します。

2 Mavenの依存関係

SpringとSpring Securityの標準的な依存関係に加えて、https://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22spring-security-oauth2-client%も追加する必要があります。 22%20AND%20g%3A%22org.springframework.security%22[ spring-security-oauth2-client ]およびhttps://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22spring- security-oauth2-jose%22[ spring-security-oauth2-jose ]依存関係:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

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

3クライアント設定

Spring Bootプロジェクトでは、構成したい各クライアントにいくつかの標準プロパティを追加するだけです。

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

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

Google OAuth2認証用のクライアント認証情報を取得するには、https://console.developers.google.com/[Google APIコンソール] - 「認証情報」セクションに進んでください。

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

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

デフォルトでは、Spring BootはこのリダイレクトURIを__/login/oauth2/client/\ {clientId}に設定します。したがって、Googleの場合はURIを追加します。

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

Facebook認証用のクライアント認証情報を取得するには、https://developers.facebook.com/docs/facebook-login[Facebook for Developers]Webサイトにアプリケーションを登録し、対応するURIを "Valid OAuth"として設定する必要がありますリダイレクトURI」:

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

3.3. セキュリティ設定

次に、 application.properties ファイルにクライアントの認証情報を追加する必要があります。 Spring Securityのプロパティは “ spring.security.oauth2.client.registration” の後にクライアント名、クライアントプロパティの名前が続きます。

spring.security.oauth2.client.registration.google.client-id=<your client id>
spring.security.oauth2.client.registration.google.client-secret=<your client secret>

spring.security.oauth2.client.registration.facebook.client-id=<your client id>
spring.security.oauth2.client.registration.facebook.client-secret=<your client secret>
  • 少なくとも1つのクライアントに対してこれらのプロパティを追加すると、必要なすべてのBeanを設定する Oauth2ClientAutoConfiguration クラス** が有効になります。

  • 自動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、width = 274、height = 119

3.4. 他のクライアント

  • GoogleとFacebookに加えて、Spring SecurityプロジェクトにはGitHubとOktaのデフォルト設定も含まれていることに注意してください。

Spring Securityで設定されていない別の認証プロバイダを使用したい場合は、承認URIやトークンURIなどの情報を使用して完全な設定を定義する必要があります。

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

4.1. ClientRegistrationRepository Beanの作成

  • Spring Bootアプリケーションを使用していない場合は、認可サーバーが所有するクライアント情報の内部表現を含む ClientRegistrationRepository bean ** を定義する必要があります。

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

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        List<ClientRegistration> 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 enumを使用します。

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());
}

ここで証明されているように、** カスタム登録リポジトリを登録するために oauth2Login() clientRegistrationRepository() メソッドを使用することができます。

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

ログインプロセスをさらにカスタマイズしていきましょう。

5 oauth2Login() をカスタマイズする

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

Spring Bootではこれらすべての要素にデフォルトの設定があり、明示的な設定は必須ではありません。

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

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

Spring Bootによってデフォルトのログインページが生成されても、通常は独自のカスタマイズページを定義します。

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

@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<String, String> oauth2AuthenticationUrls
      = new HashMap<>();

    @Autowired
    private ClientRegistrationRepository clientRegistrationRepository;

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

        return "oauth__login";
    }
}
  • このメソッドは、利用可能なクライアントとその認証エンドポイントのマップをビューに送信する必要があります。** これは、 ClientRegistrationRepository Beanから取得します。

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

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

    return "oauth__login";
}

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

<h3>Login with:</h3>
<p th:each="url : ${urls}">
    <a th:text="${url.key}" th:href="${url.value}">Client</a>
</p>

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

それにいくつかのスタイルを追加した後、私たちははるかに見栄えの良いログインページを持つことができます。

画像、幅= 300、高さ= 210

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が外部サーバーへの承認要求をトリガーするために使用するエンドポイントです。

まず、 承認エンドポイントの新しいプロパティを設定しましょう :

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

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

@Bean
public AuthorizationRequestRepository<OAuth2AuthorizationRequest>
  authorizationRequestRepository() {

    return new HttpSessionOAuth2AuthorizationRequestRepository();
}

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

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

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

  • デフォルトのレスポンスクライアント実装で tokenEndpoint() ** を明示的に設定しましょう。

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

そして、これがレスポンスクライアントBeanです。

@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>
  accessTokenResponseClient() {

    return new NimbusAuthorizationCodeTokenResponseClient();
}

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

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

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

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

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

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

デフォルトのURIは__login/oauth2/clientです。

変更する場合は、各 ClientRegistration redirectUriTemplate プロパティも更新し、新しいURIを各クライアントの承認されたリダイレクトURIとして追加する必要があります。

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

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

  • このエンドポイントは userInfoEndpoint() メソッドを使ってカスタマイズできます。

これには、 userService() customUserType() などのメソッドを使用して、ユーザー情報の取得方法を変更します。

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

私たちが達成したいと思うかもしれない一般的なタスクはログインしているユーザーについての情報を見つけることです。このために、ユーザー情報エンドポイントに要求を出すことができます。

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

@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 <map>response = restTemplate
      .exchange(userInfoEndpointUri, HttpMethod.GET, entity, Map.class);
    Map userAttributes = response.getBody();
    model.addAttribute("name", userAttributes.get("name"));
}

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

画像、幅= 300、高さ= 59

nameの他に、 userAttributes Map には、 email、family name、 __picture、localeなどのプロパティも含まれています。

7. 結論

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

例の完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-5-security-oauth[GitHubについて]で見つけることができます。

前の投稿:java.util.Propertiesオブジェクトのマージ
次の投稿:Mavenによるマルチモジュールプロジェクト