1前書き

Spring WebアプリケーションやREST APIをセキュリティで保護するときのほとんどの場合、Spring Securityが提供するツールは十分すぎるものですが、より具体的な動作を探している場合もあります。

このチュートリアルでは、カスタムの

AccessDecisionVoter

を作成し、それを使用してWebアプリケーションの認証ロジックを抽象化し、それをアプリケーションのビジネスロジックから分離する方法を説明します。

2.

シナリオ


AccessDecisionVoter

がどのように機能するかを説明するために、

USER

と__ADMINの2つのユーザータイプを使用したシナリオを実装します。


3

AccessDecisionVoter

実装

最初に、承認に関する最終決定を下す際に、カスタム投票者と一緒に参加する、Springが提供する実装のいくつかについて説明します。それからカスタム投票者を実装する方法を見てみましょう。


3.1. デフォルト

AccessDecisionVoter

実装

Spring Securityはいくつかの

AccessDecisionVoter

実装を提供しています。

ここでは、セキュリティソリューションの一部としてそれらのいくつかを使用します。

これらのデフォルトの有権者の実装がいつどのように投票するのかを見てみましょう。


AuthenticatedVoter

は、

Authentication

オブジェクトの認証レベルに基づいて投票を行います。具体的には、完全に認証されたプリンシパル、remember-meで認証されたプリンシパル、または最後に匿名のいずれかを探します。


RoleVoter

は、構成属性のいずれかが文字列

“ ROLE

”で始まる場合に投票します。もしそうであれば、

Authentication

オブジェクトの

GrantedAuthority

リストで役割を検索します。


WebExpressionVoter

を使用すると、

@ PreAuthorize

アノテーションを使用してSpEL(Spring Expression Language)を使用してリクエストを承認できます。

たとえば、Java configを使用しているとします。

@Override
protected void configure(final HttpSecurity http) throws Exception {
    ...
    .antMatchers("/").hasAnyAuthority("ROLE__USER")
    ...
}

またはXML設定を使用します –

http

タグ内の

intercept-url

タグ内でSpELを使用できます。

<http use-expressions="true">
    <intercept-url pattern="/"
      access="hasAuthority('ROLE__USER')"/>
    ...
</http>


3.2. カスタム

AccessDecisionVoter

実装

それでは、

AccessDecisionVoter

インターフェースを実装して、カスタム投票者を作成しましょう。

public class MinuteBasedVoter implements AccessDecisionVoter {
   ...
}

私たちが提供しなければならない3つのメソッドのうちの最初のものは

vote

メソッドです。

vote

メソッドはカスタム投票者の最も重要な部分であり、承認ロジックが行われる場所です。


vote

メソッドは3つの可能な値を返すことができます。


  • ACCESS

    GRANTED__ – 有権者は肯定的な回答をします


  • ACCESS

    DENIED__ – 有権者は否定的な回答をします


  • ACCESS

    ABSTAIN__ – 投票者は投票を控えます

それでは

vote

メソッドを実装しましょう。

@Override
public int vote(
  Authentication authentication, Object object, Collection collection) {
    return authentication.getAuthorities().stream()
      .map(GrantedAuthority::getAuthority)
      .filter(r -> "ROLE__USER".equals(r)
        && LocalDateTime.now().getMinute() % 2 != 0)
      .findAny()
      .map(s -> ACCESS__DENIED)
      .orElseGet(() -> ACCESS__ABSTAIN);
}

私たちの

vote

メソッドでは、リクエストが

USER

から来ているかどうかをチェックします。もしそうであれば、偶数分ならば

ACCESS

GRANTED

を返し、そうでなければ

ACCESS__DENIEDを返します。

2番目のメソッドは、投票者が特定の設定属性をサポートしているかどうかを返します。この例では、投票者はカスタム設定属性を必要としないので、

true

を返します。

@Override
public boolean supports(ConfigAttribute attribute) {
    return true;
}

3番目のメソッドは、投票者が保護されたオブジェクトタイプに投票できるかどうかを返します。私たちの投票者は保護されたオブジェクトタイプを気にしないので、

true

を返します。

@Override
public boolean supports(Class clazz) {
    return true;
}


4

AccessDecisionManager


最終的な許可決定は

AccessDecisionManager

によって処理されます。


AbstractAccessDecisionManager

には

__AccessDecisionVoter

__sのリストが含まれています – これらは互いに独立して投票を行う責任があります。

最も一般的なユースケースをカバーするために投票を処理するための3つの実装があります。


  • AffirmativeBased

    – 次のいずれかがあればアクセスを許可します。



_AccessDecisionVoter


sは肯定投票を返します
**

ConsensusBased_

– もっと肯定的な投票がある場合にアクセスを許可します

否定的である(棄権しているユーザーを無視する)
**

UnanimousBased

– すべての投票者が棄権した場合、

賛成投票を返します

もちろん、あなたのカスタム意思決定ロジックであなた自身の

AccessDecisionManager

を実装することができます。


5構成

チュートリアルのこの部分では、カスタムの

AccessDecisionVoter



AccessDecisionManager

で設定するためのJavaベースおよびXMLベースのメソッドについて見ていきます。


5.1. Javaの設定

Spring Web Security用の設定クラスを作成しましょう。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}

そして、カスタマイズされた有権者のリストとともに

UnanimousBased

マネージャーを使用する

AccessDecisionManager

Beanを定義しましょう。

@Bean
public AccessDecisionManager accessDecisionManager() {
    List<AccessDecisionVoter<? extends Object>> decisionVoters
      = Arrays.asList(
        new WebExpressionVoter(),
        new RoleVoter(),
        new AuthenticatedVoter(),
        new MinuteBasedVoter());
    return new UnanimousBased(decisionVoters);
}

最後に、以前に定義したBeanをデフォルトの

AccessDecisionManager

として使用するようにSpring Securityを設定しましょう。

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


5.2. XML設定

XML設定を使用している場合は、

spring-security.xml

ファイル(またはセキュリティ設定が含まれているファイル)を変更する必要があります。

まず、

<http>

タグを変更する必要があります。

<http access-decision-manager-ref="accessDecisionManager">
  <intercept-url
    pattern="/** ** "
    access="hasAnyRole('ROLE__ADMIN', 'ROLE__USER')"/>
  ...
</http>

次に、カスタム投票者用のBeanを追加します。

<beans:bean
  id="minuteBasedVoter"
  class="org.baeldung.voter.MinuteBasedVoter"/>

その後、

AccessDecisionManager

のBeanを追加します。

<beans:bean
  id="accessDecisionManager"
  class="org.springframework.security.access.vote.UnanimousBased">
    <beans:constructor-arg>
        <beans:list>
            <beans:bean class=
              "org.springframework.security.web.access.expression.WebExpressionVoter"/>
            <beans:bean class=
              "org.springframework.security.access.vote.AuthenticatedVoter"/>
            <beans:bean class=
              "org.springframework.security.access.vote.RoleVoter"/>
            <beans:bean class=
              "org.baeldung.voter.MinuteBasedVoter"/>
        </beans:list>
    </beans:constructor-arg>
</beans:bean>

シナリオをサポートする

<authentication-manager>

タグのサンプルは次のとおりです。

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user" password="pass" authorities="ROLE__USER"/>
            <user name="admin" password="pass" authorities="ROLE__ADMIN"/>
        </user-service>
    </authentication-provider>
</authentication-manager>

JavaとXMLの設定を組み合わせて使用​​している場合は、XMLを設定クラスにインポートできます。

@Configuration
@ImportResource({"classpath:spring-security.xml"})
public class XmlSecurityConfig {
    public XmlSecurityConfig() {
        super();
    }
}

6.

まとめ

このチュートリアルでは、

__ AccessDecisionVoter


を使用してSpring Webアプリケーションのセキュリティをカスタマイズする方法を調べました。私達は私達の解決に貢献したSpring Securityによって提供された何人かの有権者を見た。次に、カスタムの

AccessDecisionVoter__を実装する方法について説明しました。

それから

AccessDecisionManager

がどのようにして最終的な承認の決定を下すかについて議論し、そしてすべての有権者が彼らの票を投じた後にSpringが提供する実装を使用してこの決定を行う方法を示しました。

次に、JavaとXMLを介して

AccessDecisionManager

を使用して

AccessDecisionVoters

のリストを構成しました。

実装はhttps://github.com/eugenp/tutorials/tree/master/spring-security-mvc-boot[Githubプロジェクト]にあります。

プロジェクトがローカルで実行されているときは、ログインページにアクセスできます。

http://localhost:8080/spring-security-custom-permissions/login


USER

の認証情報は「user」と「pass」で、

ADMIN

の認証情報は「admin」と「pass」です。