1. 概要

このチュートリアルでは、Spring BootおよびSpringセキュリティOAuthを使用して、ユーザー認証をサードパーティおよびカスタム認証サーバーに委任するアプリケーションを作成する方法を説明します。

また、は、SpringのPrincipalExtractorおよびAuthoritiesExtractorインターフェイスを使用してプリンシパルとオーソリティの両方を抽出する方法を示します。

Spring Security OAuth2の概要については、これらのの記事を参照してください。

2. Mavenの依存関係

開始するには、spring-security-oauth2-autoconfigure依存関係をpom.xmlに追加する必要があります。

<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

3. Githubを使用したOAuth認証

次に、アプリケーションのセキュリティ構成を作成しましょう。

@Configuration
@EnableOAuth2Sso
public class SecurityConfig 
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) 
      throws Exception {
 
        http.antMatcher("/**")
          .authorizeRequests()
          .antMatchers("/login**")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin().disable();
    }
}

つまり、誰でも / login エンドポイントにアクセスでき、他のすべてのエンドポイントにはユーザー認証が必要になるということです。

また、構成クラスに @EnableOAuthSso の注釈を付けました。これにより、アプリケーションがOAuthクライアントに変換され、そのように動作するために必要なコンポーネントが作成されます。

Springはデフォルトでほとんどのコンポーネントを作成しますが、それでもいくつかのプロパティを構成する必要があります。

security.oauth2.client.client-id=89a7c4facbb3434d599d
security.oauth2.client.client-secret=9b3b08e4a340bd20e866787e4645b54f73d74b6a
security.oauth2.client.access-token-uri=https://github.com/login/oauth/access_token
security.oauth2.client.user-authorization-uri=https://github.com/login/oauth/authorize
security.oauth2.client.scope=read:user,user:email
security.oauth2.resource.user-info-uri=https://api.github.com/user

ユーザーアカウント管理を扱う代わりに、サードパーティ(この場合はGithub)に委任しているため、アプリケーションのロジックに集中できます。

4. プリンシパルとオーソリティの抽出

OAuthクライアントとして機能し、サードパーティを介してユーザーを認証する場合、考慮する必要のある3つのステップがあります。

  1. ユーザー認証–ユーザーはサードパーティで認証します
  2. ユーザー承認–認証に続いて、ユーザーがアプリケーションに代わって特定の操作を実行することを許可したときです。 ここでスコープが登場します
  3. ユーザーデータの取得–取得したOAuthトークンを使用してユーザーのデータを取得します

ユーザーのデータを取得すると、Springはユーザーのプリンシパルとオーソリティを自動的に作成できるようになります。

それは許容できるかもしれませんが、多くの場合、私たちはそれらを完全に制御したいシナリオに自分自身を見つけます。

そのために、 Springは、デフォルトの動作をオーバーライドするために使用できる2つのインターフェイスを提供します。

  • PrincipalExtractor Principalを抽出するためのカスタムロジックを提供するために使用できるインターフェイス
  • AuthorityExtractor PrincipalExtractor に似ていますが、代わりにAuthority抽出をカスタマイズするために使用されます

デフォルトでは、 Springは、FixedPrincipalExtractorとFixedAuthoritiesExtractor の2つのコンポーネントを提供します。これらのコンポーネントは、これらのインターフェイスを実装し、それらを作成するための事前定義された戦略を備えています。

4.1. Githubの認証のカスタマイズ

私たちの場合、Githubのユーザーデータがのようにどのように見えるか、そしてニーズに応じてそれらを調整するために何を使用できるかを認識しています。

そのため、Springのデフォルトのコンポーネントをオーバーライドするには、これらのインターフェースも実装する2つのBeansを作成する必要があります。

アプリケーションのPrincipalには、ユーザーのGithubユーザー名を使用するだけです。

public class GithubPrincipalExtractor 
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map<String, Object> map) {
        return map.get("login");
    }
}

ユーザーのGithubサブスクリプションに応じて(無料またはその他)、GITHUB_USER_SUBSCRIBEDまたはGITHUB_USER_FREE権限をユーザーに付与します。

public class GithubAuthoritiesExtractor 
  implements AuthoritiesExtractor {
    List<GrantedAuthority> GITHUB_FREE_AUTHORITIES
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_FREE");
    List<GrantedAuthority> GITHUB_SUBSCRIBED_AUTHORITIES 
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_SUBSCRIBED");

    @Override
    public List<GrantedAuthority> extractAuthorities
      (Map<String, Object> map) {
 
        if (Objects.nonNull(map.get("plan"))) {
            if (!((LinkedHashMap) map.get("plan"))
              .get("name")
              .equals("free")) {
                return GITHUB_SUBSCRIBED_AUTHORITIES;
            }
        }
        return GITHUB_FREE_AUTHORITIES;
    }
}

次に、これらのクラスを使用してBeanを作成する必要もあります。

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    // ...

    @Bean
    public PrincipalExtractor githubPrincipalExtractor() {
        return new GithubPrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor githubAuthoritiesExtractor() {
        return new GithubAuthoritiesExtractor();
    }
}

4.2. カスタム認証サーバーの使用

サードパーティに依存する代わりに、ユーザー用に独自の認証サーバーを使用することもできます。

使用することを決定した認証サーバーにもかかわらず、PrincipalAuthoritiesの両方をカスタマイズする必要があるコンポーネントは同じままです:PrincipalExtractorAuthoritiesExtractor[ X214X]。

user-info-uriエンドポイントから返されるデータを認識し、適切と思われる方法で使用する必要があります。

thisの記事で説明されている認証サーバーを使用してユーザーを認証するようにアプリケーションを変更してみましょう。

security.oauth2.client.client-id=SampleClientId
security.oauth2.client.client-secret=secret
security.oauth2.client.access-token-uri=http://localhost:8081/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8081/auth/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:8081/auth/user/me

承認サーバーをポイントしているので、両方のエクストラクターを作成する必要があります。 この場合、 PrincipalExtractor は、 name キーを使用して、MapからPrincipalを抽出します。

public class BaeldungPrincipalExtractor 
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map<String, Object> map) {
        return map.get("name");
    }
}

権限に関しては、承認サーバーはすでにuser-info-uriのデータにそれらを配置しています。

そのため、それらを抽出して強化します。

public class BaeldungAuthoritiesExtractor 
  implements AuthoritiesExtractor {

    @Override
    public List<GrantedAuthority> extractAuthorities
      (Map<String, Object> map) {
        return AuthorityUtils
          .commaSeparatedStringToAuthorityList(asAuthorities(map));
    }

    private String asAuthorities(Map<String, Object> map) {
        List<String> authorities = new ArrayList<>();
        authorities.add("BAELDUNG_USER");
        List<LinkedHashMap<String, String>> authz = 
          (List<LinkedHashMap<String, String>>) map.get("authorities");
        for (LinkedHashMap<String, String> entry : authz) {
            authorities.add(entry.get("authority"));
        }
        return String.join(",", authorities);
    }
}

次に、BeanをSecurityConfigクラスに追加します。

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public PrincipalExtractor baeldungPrincipalExtractor() {
        return new BaeldungPrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor baeldungAuthoritiesExtractor() {
        return new BaeldungAuthoritiesExtractor();
    }
}

5. 結論

この記事では、ユーザー認証をサードパーティとカスタム承認サーバーに委任するアプリケーションを実装し、プリンシパル権限の両方をカスタマイズする方法を示しました。

いつものように、この例の実装はGithubにあります。

ローカルで実行する場合は、 localhost:8082でアプリケーションを実行してテストできます。