SpringSecurityのカスタムAccessDecisionVoters
1. 序章
ほとんどの場合、SpringWebアプリケーションまたはRESTAPIを保護する場合、Spring Securityが提供するツールで十分ですが、より具体的な動作を探している場合もあります。
このチュートリアルでは、カスタム AccessDecisionVoter を作成し、それを使用してWebアプリケーションの承認ロジックを抽象化し、アプリケーションのビジネスロジックから分離する方法を示します。
2. シナリオ
AccessDecisionVoter がどのように機能するかを示すために、 USERとADMIN、の2つのユーザータイプで USER ADMIN は常にアクセスを許可されますが、偶数分にのみシステムにアクセスできます。
3. AccessDecisionVoter実装
最初に、Springによって提供されるいくつかの実装について説明します。これらの実装は、承認の最終決定を行う際にカスタム投票者と一緒に参加します。 次に、カスタム投票者を実装する方法を見ていきます。
3.1. デフォルトのAccessDecisionVoter実装
Spring Securityは、いくつかのAccessDecisionVoter実装を提供します。 ここでは、セキュリティソリューションの一部としてそれらのいくつかを使用します。
これらのデフォルトの有権者の実装がいつどのように投票するかを見てみましょう。
AuthenticatedVoter は、 Authentication オブジェクトの認証レベルに基づいて投票します。具体的には、完全に認証されたプリンシパル、remember-meで認証されたプリンシパル、または最後に匿名のいずれかを探します。
RoleVoter は、構成属性のいずれかがString “ ROLE _”で始まる場合に投票します。その場合、GrantedAuthorityリストで役割を検索します。 認証オブジェクト。
WebExpressionVoter を使用すると、SpEL(Spring Expression Language)を使用して、@PreAuthorizeアノテーションを使用してリクエストを承認できます。
たとえば、Javaconfigを使用している場合:
@Override
protected void configure(final HttpSecurity http) throws Exception {
...
.antMatchers("/").hasAnyAuthority("ROLE_USER")
...
}
または、XML構成を使用します– Intercept-urlタグ内のhttpタグ内で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メソッドは次の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 偶数の分であれば、それ以外の場合は戻ります
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 のリストが含まれています。これらは、互いに独立して投票する責任があります。
最も一般的なユースケースをカバーするために投票を処理するための3つの実装があります。
- AffirmativeBased – AccessDecisionVoterのいずれかが賛成票を返した場合にアクセスを許可します
- ConsensusBased –反対票よりも賛成票の方が多い場合にアクセスを許可します(棄権したユーザーを無視します)
- UnanimousBased –すべての有権者が反対票を棄権または返還した場合にアクセスを許可します
もちろん、カスタムの意思決定ロジックを使用して、独自のAccessDecisionManagerを実装できます。
5. 構成
チュートリアルのこの部分では、カスタムAccessDecisionVoterをAccessDecisionManagerで構成するためのJavaベースおよびXMLベースのメソッドを見ていきます。
5.1. Java構成
SpringWebセキュリティの構成クラスを作成しましょう。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
そして、カスタマイズされた投票者リストでUnanimousBasedマネージャーを使用するAccessDecisionManagerbeanを定義しましょう。
@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セキュリティを構成しましょう。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.anyRequest()
.authenticated()
.accessDecisionManager(accessDecisionManager());
}
5.2. XML構成
XML構成を使用している場合は、 spring -security.xml ファイル(またはセキュリティ設定が含まれているファイル)を変更する必要があります。
まず、変更する必要があります
<http access-decision-manager-ref="accessDecisionManager">
<intercept-url
pattern="/**"
access="hasAnyRole('ROLE_ADMIN', 'ROLE_USER')"/>
...
</http>
次に、カスタム投票者用のBeanを追加します。
<beans:bean
id="minuteBasedVoter"
class="com.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=
"com.baeldung.voter.MinuteBasedVoter"/>
</beans:list>
</beans:constructor-arg>
</beans:bean>
これがサンプルです
<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 を使用して、SpringWebアプリケーションのセキュリティをカスタマイズする方法について説明しました。 私たちのソリューションに貢献したSpringSecurityから提供された有権者を見ました。 次に、カスタムAccessDecisionVoterを実装する方法について説明しました。
次に、 AccessDecisionManager が最終的な承認決定を行う方法について説明し、すべての有権者が投票した後、Springが提供する実装を使用してこの決定を行う方法を示しました。
次に、JavaとXMLを介してAccessDecisionManagerを使用してAccessDecisionVotersのリストを構成しました。
実装はGithubプロジェクトにあります。
プロジェクトがローカルで実行されている場合、ログインページには次の場所からアクセスできます。
http://localhost:8082/login
USER のクレデンシャルは「user」と「pass」であり、ADMINのクレデンシャルは「admin」と「pass」です。