1. 概要

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

このチュートリアルでは、 oauth2Login()要素で使用できるさまざまな構成オプションのいくつかについて説明します。

2. Mavenの依存関係

Spring Bootプロジェクトでは、スターターspring-boot-starter-oauth2-clientを追加する必要があります。

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

非ブートプロジェクトでは、標準のSpringおよびSpring Securityの依存関係に加えて、spring-security-oauth2-clientおよびspring-security-oauth2-も明示的に追加する必要があります。 jose の依存関係:

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

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

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

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

3.1. クライアントクレデンシャルの取得

Google OAuth2認証のクライアントクレデンシャルを取得するには、GoogleAPIコンソールの「クレデンシャル」セクションに進んでください。

ここでは、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.2. セキュリティ構成

次に、クライアントの資格情報を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つのクライアントで自動生成されたログインページを表示します

3.3. 他のクライアント

Spring Securityプロジェクトには、GoogleとFacebookに加えて、GitHubとOktaのデフォルト構成も含まれていることに注意してください。これらのデフォルト構成は、認証に必要なすべての情報を提供します。これにより、クライアントの資格情報のみを入力できます。

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

4. 非起動プロジェクトでのセットアップ

4.1. ClientRegistrationRepositoryBeanの作成

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

@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クライアントの残りのクライアントプロパティに、SpringSecurityですでに定義されているCommonOauth2Provider列挙型を使用します。

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

4.3. ClientRegistrationRepositoryの登録

最後に、 ClientRegistrationRepositoryBeanに基づいて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";
    }
}

このメソッドは、使用可能なクライアントとその承認エンドポイントのマップをビューに送信する必要があります。これは、ClientRegistrationRepositoryBeanから取得します。

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ページです。

それにスタイルを追加した後、ログインページの外観を変更できます。

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

さまざまな方法で認証後の動作を制御できます。

  • defaultSuccessUrl()および failureUrl()を使用して、ユーザーを特定のURLにリダイレクトします
  • successHandler()および failureHandler()は、認証プロセスに続いてカスタムロジックを実行します

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

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

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

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

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

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

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

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

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

ここでは、baseUriをデフォルトの/oauth2 /authenticationではなく/oauth2 /authorize-clientに変更しました。

また、定義する必要のある authenticationRequestRepository()Beanを明示的に設定しています。

@Bean
public AuthorizationRequestRepository<OAuth2AuthorizationRequest> 
  authorizationRequestRepository() {
 
    return new HttpSessionOAuth2AuthorizationRequestRepository();
}

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

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

tokenエンドポイントはアクセストークンを処理します。

デフォルトの応答クライアント実装を使用して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 /codeです。

これを変更する場合は、各ClientRegistrationredirectUriTemplateプロパティも更新し、新しい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";
}

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

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ビューにユーザーへのウェルカムメッセージとして表示できます。

name の他に、 userAttributes Map には、 email family_name picture などのプロパティも含まれています。 ]ロケール

7. 結論

この記事では、SpringSecurityのoauth2Login()要素を使用して、GoogleやFacebookなどのさまざまなプロバイダーで認証する方法を説明しました。

また、このプロセスをカスタマイズするためのいくつかの一般的なシナリオも実行しました。

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