Spring SecurityのカスタムAccessDecisionVoters
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」です。