最新の安定バージョンについては、Spring Security 6.3.1 を使用してください! |
認可の移行
次の手順は、認可の実行方法に関する変更に関連しています。
メソッドセキュリティに AuthorizationManager
を使用する
メソッドのセキュリティは、AuthorizationManager
API と Spring AOP の直接使用によって簡素化されました。
これらの変更を行う際に問題が発生した場合、@EnableGlobalMethodSecurity
は非推奨ですが、6.0 では削除されないことに注意してください。古いアノテーションを使用することでオプトアウトできます。
グローバルメソッドセキュリティをメソッドセキュリティに置き換える
@EnableGlobalMethodSecurity
(Javadoc) と <global-method-security>
は非推奨となり、それぞれ @EnableMethodSecurity
(Javadoc) と <method-security>
が推奨されます。新しいアノテーションと XML 要素は、デフォルトで Spring の事前投稿アノテーションを有効にし、内部で AuthorizationManager
を使用します。
これは、次の 2 つのリストが関数に同等であることを意味します。
Java
Kotlin
XML
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
<global-method-security pre-post-enabled="true"/>
および:
Java
Kotlin
XML
@EnableMethodSecurity
@EnableMethodSecurity
<method-security/>
プリポストアノテーションを使用しないアプリケーションの場合は、望ましくない動作がアクティブにならないように、必ずオフにしてください。
例: 次のようなリスト:
Java
Kotlin
XML
@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableGlobalMethodSecurity(securedEnabled = true)
<global-method-security secured-enabled="true"/>
次のように変更する必要があります。
Java
Kotlin
XML
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
<method-security secured-enabled="true" pre-post-enabled="false"/>
@EnableTransactionManagement
の order
値を変更します
@EnableTransactionManagement
と @EnableGlobalMethodSecurity
は同じ order
値 Integer.MAX_VALUE
を持ちます。これは、Spring AOP Advisor チェーン 内での相互の順序が未定義であることを意味します。
ほとんどのメソッドセキュリティ式は正しく機能するためにオープントランザクションを必要としないため、これは多くの場合問題ありません。ただし、歴史的には、order
値を設定することで、一方が他方よりも先に発生するようにする必要がある場合がありました。
@EnableMethodSecurity
は複数のインターセプターを公開するため、order
値を持ちません。実際、すべてのインターセプターを同じアドバイザーチェーン の場所に設定できないため、@EnableTransactionManagement
との下位互換性を試みることはできません。
代わりに、@EnableMethodSecurity
インターセプターの値はオフセット 0 に基づいています。@PreFilter
インターセプターの順序は 100、@PostAuthorize
は 200 などです。
更新後に、オープンなトランザクションがないためにメソッドのセキュリティ式が機能していないことが判明した場合は、トランザクションアノテーション定義を次から変更してください。
Java
Kotlin
XML
@EnableTransactionManagement
@EnableTransactionManagement
<tx:annotation-driven ref="txManager"/>
to:
Java
Kotlin
XML
@EnableTransactionManagement(order = 0)
@EnableTransactionManagement(order = 0)
<tx:annotation-driven ref="txManager" order="0"/>
このようにして、トランザクション AOP アドバイスが Spring Security のアドバイスの前に配置され、認可 SpEL 式が評価されるときにトランザクションがオープンされます。
DefaultMethodSecurityExpressionHandler
をサブクラス化する代わりにカスタム @Bean
を使用する
パフォーマンスの最適化として、Authentication
の代わりに Supplier<Authentication>
を取る新しいメソッドが MethodSecurityExpressionHandler
に導入されました。
これにより、Spring Security は Authentication
のルックアップを延期でき、@EnableGlobalMethodSecurity
の代わりに @EnableMethodSecurity
を使用すると自動的に利用されます。
ただし、コードが DefaultMethodSecurityExpressionHandler
を継承し、createSecurityExpressionRoot(Authentication, MethodInvocation)
をオーバーライドしてカスタム SecurityExpressionRoot
インスタンスを返すとします。これは、@EnableMethodSecurity
がセットアップする配置が代わりに createEvaluationContext(Supplier<Authentication>, MethodInvocation)
を呼び出すため、機能しなくなります。
幸いなことに、このようなレベルのカスタマイズは不要なことがよくあります。代わりに、必要な認証方法を使用してカスタム Bean を作成できます。
例: @PostAuthorize("hasAuthority('ADMIN')")
のカスタム評価が必要だとしましょう。次のようなカスタム @Bean
を作成できます。
Java
Kotlin
class MyAuthorizer {
boolean isAdmin(MethodSecurityExpressionOperations root) {
boolean decision = root.hasAuthority("ADMIN");
// custom work ...
return decision;
}
}
class MyAuthorizer {
fun isAdmin(val root: MethodSecurityExpressionOperations): boolean {
val decision = root.hasAuthority("ADMIN");
// custom work ...
return decision;
}
}
そして、次のようにアノテーションで参照します。
Java
Kotlin
@PreAuthorize("@authz.isAdmin(#root)")
@PreAuthorize("@authz.isAdmin(#root)")
DefaultMethodSecurityExpressionHandler
をサブクラス化したい
DefaultMethodSecurityExpressionHandler
のサブクラス化を継続する必要がある場合でも、そうすることができます。代わりに、次のように createEvaluationContext(Supplier<Authentication>, MethodInvocation)
メソッドをオーバーライドします。
Java
Kotlin
@Component
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
@Override
public EvaluationContext createEvaluationContext(
Supplier<Authentication> authentication, MethodInvocation mi) {
StandardEvaluationContext context = (StandardEvaluationContext) super.createEvaluationContext(authentication, mi);
MySecurityExpressionRoot root = new MySecurityExpressionRoot(authentication, invocation);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(new AuthenticationTrustResolverImpl());
root.setRoleHierarchy(getRoleHierarchy());
context.setRootObject(root);
return context;
}
}
@Component
class MyExpressionHandler: DefaultMethodSecurityExpressionHandler {
override fun createEvaluationContext(val authentication: Supplier<Authentication>,
val mi: MethodInvocation): EvaluationContext {
val context = super.createEvaluationContext(authentication, mi) as StandardEvaluationContext;
val root = new MySecurityExpressionRoot(authentication, invocation);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(new AuthenticationTrustResolverImpl());
root.setRoleHierarchy(getRoleHierarchy());
context.setRootObject(root);
return context;
}
}
PermissionEvaluator
の代わりに MethodSecurityExpressionHandler
を発行する
@EnableMethodSecurity
は PermissionEvaluator
をピックアップしません。これにより、API をシンプルに保つことができます。
カスタム PermissionEvaluator
(Javadoc) @Bean
がある場合は、次のように変更してください。
Java
Kotlin
@Bean
static PermissionEvaluator permissionEvaluator() {
// ... your evaluator
}
companion object {
@Bean
fun permissionEvaluator(): PermissionEvaluator {
// ... your evaluator
}
}
to:
Java
Kotlin
@Bean
static MethodSecurityExpressionHandler expressionHandler() {
var expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
return expressionHandler;
}
companion object {
@Bean
fun expressionHandler(): MethodSecurityExpressionHandler {
val expressionHandler = DefaultMethodSecurityExpressionHandler
expressionHandler.setPermissionEvaluator(myPermissionEvaluator)
return expressionHandler
}
}
カスタムメソッドセキュリティ AccessDecisionManager
を置き換えます
アプリケーションには、カスタム AccessDecisionManager
(Javadoc) または AccessDecisionVoter
(Javadoc) 配置がある場合があります。準備戦略は、各取り決めの理由によって異なります。あなたの状況に最適な方法を見つけるために参照してください。
UnanimousBased
を使っています
アプリケーションがデフォルトの投票者で UnanimousBased
(Javadoc) を使用する場合、@EnableMethodSecurity
(Javadoc) では全会一致ベースがデフォルトの動作であるため、おそらく何もする必要はありません。
ただし、デフォルトの認証マネージャーを受け入れることができないことがわかった場合は、AuthorizationManagers.allOf
を使用して独自の取り決めを作成できます。
allOf
とは異なり、すべての参加者が棄権した場合に認可が与えられることに注意してください。すべてのデリゲートが棄権したときに認可を拒否する必要がある場合は、一連のデリゲート AuthorizationManager
を考慮した複合 AuthorizationManager
(Javadoc) を実装してください。
それが完了したら、カスタム AuthorizationManager
を追加するためのリファレンスマニュアルの詳細に従ってください。
AffirmativeBased
を使っています
アプリケーションが AffirmativeBased
(Javadoc) を使用している場合、次のように同等の AuthorizationManager
(Javadoc) を作成できます。
Java
Kotlin
AuthorizationManager<MethodInvocation> authorization = AuthorizationManagers.anyOf(
// ... your list of authorization managers
)
val authorization = AuthorizationManagers.anyOf(
// ... your list of authorization managers
)
AuthorizationManager
を実装したら、リファレンスマニュアルの詳細に従ってカスタム AuthorizationManager
を追加してください。
ConsensusBased
を使っています
フレームワークが提供する ConsensusBased
(Javadoc) に相当するものはありません。その場合、デリゲート AuthorizationManager
のセットを考慮した複合 AuthorizationManager
(Javadoc) を実装してください。
AuthorizationManager
を実装したら、リファレンスマニュアルの詳細に従ってカスタム AuthorizationManager
を追加してください。
カスタム AccessDecisionVoter
を使用しています
クラスを変更して AuthorizationManager
(Javadoc) を実装するか、アダプターを作成する必要があります。
カスタム投票者が何をしているかを知らなければ、汎用ソリューションを推奨することはできません。ただし、例として、SecurityMetadataSource
(Javadoc) と AccessDecisionVoter
(Javadoc) を @PreAuthorize
に適合させると、次のようになります。
Java
public final class PreAuthorizeAuthorizationManagerAdapter implements AuthorizationManager<MethodInvocation> {
private final SecurityMetadataSource metadata;
private final AccessDecisionVoter voter;
public PreAuthorizeAuthorizationManagerAdapter(MethodSecurityExpressionHandler expressionHandler) {
ExpressionBasedAnnotationAttributeFactory attributeFactory =
new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
this.metadata = new PrePostAnnotationSecurityMetadataSource(attributeFactory);
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(expressionHandler);
this.voter = new PreInvocationAuthorizationAdviceVoter(expressionAdvice);
}
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
List<ConfigAttribute> attributes = this.metadata.getAttributes(invocation, AopUtils.getTargetClass(invocation.getThis()));
int decision = this.voter.vote(authentication.get(), invocation, attributes);
if (decision == ACCESS_GRANTED) {
return new AuthorizationDecision(true);
}
if (decision == ACCESS_DENIED) {
return new AuthorizationDecision(false);
}
return null; // abstain
}
}
AuthorizationManager
を実装したら、リファレンスマニュアルの詳細に従ってカスタム AuthorizationManager
を追加してください。
AfterInvocationManager
または AfterInvocationProvider
を使用しています
AfterInvocationManager
(Javadoc) と AfterInvocationProvider
(Javadoc) は、呼び出しの結果について認可の決定を行います。例: メソッド呼び出しの場合、これらはメソッドの戻り値に関する認可決定を行います。
Spring Security 3.0 では、認可の意思決定が @PostAuthorize
および @PostFilter
アノテーションに標準化されました。@PostAuthorize
は、戻り値全体が返されたかどうかを判断するためのものです。@PostFilter
は、返されたコレクション、配列、ストリームから個々のエントリをフィルタリングするためのものです。
AfterInvocationProvider
と AfterInvocationManager
は現在非推奨であるため、これらの 2 つのアノテーションはほとんどのニーズに対応する必要があり、いずれかまたは両方に移行することをお勧めします。
独自の AfterInvocationManager
または AfterInvocationProvider
を実装した場合は、まずそれが何をしようとしているのかを自問する必要があります。戻り値の型を承認しようとしている場合は、AuthorizationManager<MethodInvocationResult>
の実装と AfterMethodAuthorizationManagerInterceptor
の使用を検討してください。または、カスタム Bean を公開し、@PostAuthorize("@myBean.authorize(#root)")
を使用します。
フィルタリングしようとしている場合は、カスタム Bean を公開し、@PostFilter("@mybean.authorize(#root)")
を使用することを検討してください。または、必要に応じて、例として PostFilterAuthorizationMethodInterceptor
および PrePostMethodSecurityConfiguration
を見て、独自の MethodInterceptor
を実装できます。
RunAsManager
を使っています
現在、RunAsManager
に代わるものはありません [GitHub] (英語) が、検討中です。
ただし、必要に応じて RunAsManager
を AuthorizationManager
API に適合させるのは非常に簡単です。
開始するための疑似コードを次に示します。
Java
public final class RunAsAuthorizationManagerAdapter<T> implements AuthorizationManager<T> {
private final RunAsManager runAs = new RunAsManagerImpl();
private final SecurityMetadataSource metadata;
private final AuthorizationManager<T> authorization;
// ... constructor
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
Supplier<Authentication> wrapped = (auth) -> {
List<ConfigAttribute> attributes = this.metadata.getAttributes(object);
return this.runAs.buildRunAs(auth, object, attributes);
};
return this.authorization.check(wrapped, object);
}
}
AuthorizationManager
を実装したら、リファレンスマニュアルの詳細に従ってカスタム AuthorizationManager
を追加してください。
メッセージセキュリティに AuthorizationManager
を使用する
メッセージセキュリティは、AuthorizationManager
API と Spring AOP の直接使用によって改善されました。
これらの変更を行う際に問題が発生した場合は、このセクションの最後にあるオプトアウトの手順に従ってください。
すべてのメッセージに認可規則が定義されていることを確認する
非推奨のメッセージセキュリティサポート (Javadoc) では、デフォルトですべてのメッセージが許可されます。新しいサポートには、すべてのメッセージを拒否する強力なデフォルトがあります。
これに備えて、すべてのリクエストに対して認可ルールが宣言されていることを確認してください。
例: 次のようなアプリケーション構成:
Java
Kotlin
XML
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN");
}
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
}
<websocket-message-broker>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>
次のように変更する必要があります。
Java
Kotlin
XML
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll();
}
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll()
}
<websocket-message-broker>
<intercept-message type="CONNECT" access="permitAll"/>
<intercept-message type="DISCONNECT" access="permitAll"/>
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>
@EnableWebSocketSecurity
を追加
CSRF を無効にする必要があり、Java 構成を使用している場合、移行手順は少し異なります。 |
Java 構成を使用している場合は、アプリケーションに @EnableWebSocketSecurity
(Javadoc) を追加します。
例: 次のように、websocket セキュリティ構成クラスに追加できます。
Java
Kotlin
@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
// ...
}
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
// ...
}
これにより、MessageMatcherDelegatingAuthorizationManager.Builder
のプロトタイプインスタンスが利用可能になり、拡張ではなく構成による構成が促進されます。
AuthorizationManager<Message<?>>
インスタンスを使用する
AuthorizationManager
の使用を開始するには、XML で use-authorization-manager
属性を設定するか、Java で AuthorizationManager<Message<?>>
@Bean
を公開します。
例: 次のアプリケーション構成:
Java
Kotlin
XML
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll();
}
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll()
}
<websocket-message-broker>
<intercept-message type="CONNECT" access="permitAll"/>
<intercept-message type="DISCONNECT" access="permitAll"/>
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>
変更:
Java
Kotlin
XML
@Bean
AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll();
return messages.build();
}
@Bean
fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll()
return messages.build()
}
<websocket-message-broker use-authorization-manager="true">
<intercept-message type="CONNECT" access="permitAll"/>
<intercept-message type="DISCONNECT" access="permitAll"/>
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>
AbstractSecurityWebSocketMessageBrokerConfigurer
の実装をやめる
Java 構成を使用している場合は、単純に WebSocketMessageBrokerConfigurer
を継承できるようになりました。
例: AbstractSecurityWebSocketMessageBrokerConfigurer
を継承するクラスが WebSocketSecurityConfig
と呼ばれる場合:
Java
Kotlin
@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
// ...
}
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
// ...
}
変更:
Java
Kotlin
@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
// ...
}
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
// ...
}
オプトアウトの手順
問題が発生した場合は、最適なオプトアウト動作について次のシナリオを参照してください。
すべてのリクエストに対して認可ルールを宣言することはできません
denyAll
の anyRequest
認可ルールの設定に問題がある場合は、代わりに permitAll
(Javadoc) を次のように使用してください。
Java
Kotlin
XML
@Bean
AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
// ...
.anyMessage().permitAll();
return messages.build();
}
@Bean
fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
// ...
.anyMessage().permitAll();
return messages.build()
}
<websocket-message-broker use-authorization-manager="true">
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<!-- ... -->
<intercept-message pattern="/**" access="permitAll"/>
</websocket-message-broker>
CSRF が機能しない、他の AbstractSecurityWebSocketMessageBrokerConfigurer
機能が必要、または AuthorizationManager
に問題がある
Java の場合は、引き続き AbstractMessageSecurityWebSocketMessageBrokerConfigurer
を使用できます。非推奨ですが、6.0 では削除されません。
XML の場合、use-authorization-manager="false"
を設定することで AuthorizationManager
をオプトアウトできます。
<websocket-message-broker>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>
to:
<websocket-message-broker use-authorization-manager="false">
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>
リクエストセキュリティに AuthorizationManager
を使用する
HTTP リクエストのセキュリティは AuthorizationManager
API によって簡素化されました。
これらの変更を行う際に問題が発生した場合は、このセクションの最後にあるオプトアウトの手順に従ってください。
すべてのリクエストに認可ルールが定義されていることを確認する
Spring Security 5.8 以前では、認可ルールのないリクエストはデフォルトで認可されます。デフォルトで拒否することはより強力なセキュリティ上の立場であるため、すべてのエンドポイントに対して認可規則を明確に定義する必要があります。そのため、6.0 では、Spring Security は既定で、認可規則が欠落しているすべてのリクエストを拒否します。
この変更に備える最も簡単な方法は、適切な anyRequest
(Javadoc) ルールを最後の認可ルールとして導入することです。denyAll
(Javadoc) が暗黙の 6.0 デフォルトであるため、推奨は denyAll
(Javadoc) です。
満足できる |
最後に denyAll
を追加すると、変更のように見えます。
Java
Kotlin
XML
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
)
// ...
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/app/**", hasRole("APP"))
// ...
}
}
<http once-per-request="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>
to:
Java
Kotlin
XML
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
<http once-per-request="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
すでに authorizeHttpRequests
に移行している場合、推奨される変更は同じです。
AuthorizationManager
に切り替え
AuthorizationManager
の使用を選択するには、Java または XML にそれぞれ authorizeHttpRequests
または use-authorization-manager
を使用できます。
変更:
Java
Kotlin
XML
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
<http once-per-request="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
to:
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(false)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = false
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
hasIpAddress
から access(AuthorizationManager)
への移行
hasIpAddress
には、authorizeHttpRequests
に相当する DSL がありません。
hasIpAddress
に呼び出されたものを AuthorizationManager
を使用するように変更する必要があります。
まず、次のように IpAddressMatcher
を構築します。
IpAddressMatcher hasIpAddress = new IpAddressMatcher("127.0.0.1");
そして、これを次のように変更します。
http
.authorizeRequests((authorize) -> authorize
.mvcMatchers("/app/**").hasIpAddress("127.0.0.1")
// ...
.anyRequest().denyAll()
)
// ...
これに:
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/app/**").access((authentication, context) ->
new AuthorizationDecision(hasIpAddress.matches(context.getRequest()))
// ...
.anyRequest().denyAll()
)
// ...
IP アドレスによるセキュリティ保護は、そもそも非常に脆弱です。そのため、このサポートを authorizeHttpRequests に移植する予定はありません。 |
SpEL 式を AuthorizationManager
に移行する
認可ルールについては、SpEL よりも Java の方がテストと保守が容易になる傾向があります。そのため、authorizeHttpRequests
には String
SpEL を宣言するメソッドがありません。
代わりに、独自の AuthorizationManager
実装を実装するか、WebExpressionAuthorizationManager
を使用できます。
完全を期すために、両方のオプションが示されます。
まず、次の SpEL がある場合:
Java
Kotlin
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/complicated/**").access("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/complicated/**", access("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
// ...
authorize(anyRequest, denyAll)
}
}
次に、次のように、Spring Security 認可プリミティブを使用して独自の AuthorizationManager
を構成できます。
Java
Kotlin
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(false)
.mvcMatchers("/complicated/**").access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = false
authorize("/complicated/**", access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
// ...
authorize(anyRequest, denyAll)
}
}
または、次の方法で WebExpressionAuthorizationManager
を使用できます。
Java
Kotlin
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/complicated/**").access(
new WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
)
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/complicated/**", access(
WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
)
// ...
authorize(anyRequest, denyAll)
}
}
すべてのディスパッチャー型をフィルタリングするように切り替えます
Spring Security 5.8 以前は、リクエストごとに 1 回だけ認可を実行します。つまり、REQUEST
の後に実行される FORWARD
や INCLUDE
などのディスパッチャー型は、デフォルトでは保護されていません。
Spring Security ですべてのディスパッチ型を保護することをお勧めします。そのため、6.0 では、Spring Security がこのデフォルトを変更します。
最後に、認可ルールを変更して、すべてのディスパッチャー型をフィルタリングします。
これを行うには、次のように変更する必要があります。
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(false)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = false
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
to:
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = true
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
また、FilterChainProxy
は、すべてのディスパッチャー型に対しても登録する必要があります。Spring Boot を使用している場合は、spring.security.filter.dispatcher-types
プロパティを変更して、すべてのディスパッチャー型を含める必要があります。
spring.security.filter.dispatcher-types=request,async,error,forward,include
AbstractSecurityWebApplicationInitializer
を使用している場合は、getSecurityDispatcherTypes
メソッドをオーバーライドして、すべてのディスパッチャー型を返す必要があります。
Java
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC,
DispatcherType.FORWARD, DispatcherType.INCLUDE);
}
}
Spring MVC 使用時に FORWARD
を許可する
ビュー名を解決するための Spring MVC を使用している場合は、FORWARD
リクエストを認可する必要があります。これは、Spring MVC がビュー名と実際のビュー間のマッピングを検出すると、ビューへの転送を実行するためです。前のセクションで説明したように、Spring Security 6.0 はデフォルトで FORWARD
リクエストに認可を適用します。
次の一般的な構成を検討してください。
Java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(true)
.requestMatchers("/").authenticated()
.anyRequest().denyAll()
)
.formLogin((form) -> form
.loginPage("/login")
.permitAll()
));
return http.build();
}
および次の同等の MVC ビューマッピング構成のいずれか:
Java
@Controller
public class MyController {
@GetMapping("/login")
public String login() {
return "login";
}
}
Java
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
どちらの構成でも、/login
へのリクエストがある場合、Spring MVC はビュー login
への転送を実行します。これは、デフォルト構成では src/main/resources/templates/login.html
パスにあります。セキュリティ構成は /login
へのリクエストを許可しますが、/templates/login.html
のビューへの FORWARD
リクエストを含め、他のすべてのリクエストは拒否されます。
これを修正するには、FORWARD
リクエストを許可するように Spring Security を構成する必要があります。
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(true)
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.anyRequest().denyAll()
)
// ...
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = true
authorize(DispatcherTypeRequestMatcher(DispatcherType.FORWARD), permitAll)
authorize(anyRequest, denyAll)
}
}
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
<intercept-url request-matcher-ref="forwardRequestMatcher" access="permitAll()" />
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
<bean name="forwardRequestMatcher" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
<constructor-arg value="FORWARD"/>
</bean>
カスタムフィルターセキュリティ AccessDecisionManager
を置き換えます
アプリケーションには、カスタム AccessDecisionManager
(Javadoc) または AccessDecisionVoter
(Javadoc) 配置がある場合があります。準備戦略は、各取り決めの理由によって異なります。あなたの状況に最適な方法を見つけるために参照してください。
UnanimousBased
を使っています
アプリケーションで UnanimousBased
(Javadoc) を使用する場合、最初に AccessDecisionVoter
を適応または置換してから、次のように AuthorizationManager
を構築する必要があります。
Java
Kotlin
XML
@Bean
AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
PolicyAuthorizationManager policy = ...;
LocalAuthorizationManager local = ...;
return AuthorizationManagers.allOf(policy, local);
}
@Bean
fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
val policy: PolicyAuthorizationManager = ...
val local: LocalAuthorizationManager = ...
return AuthorizationManagers.allOf(policy, local)
}
<bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
factory-method="allOf">
<constructor-arg>
<util:list>
<bean class="my.PolicyAuthorizationManager"/>
<bean class="my.LocalAuthorizationManager"/>
</util:list>
</constructor-arg>
</bean>
次に、次のように DSL に接続します。
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
// ...
http {
authorizeHttpRequests {
authorize(anyRequest, requestAuthorization)
}
// ...
}
<http authorization-manager-ref="requestAuthorization"/>
|
AffirmativeBased
を使っています
アプリケーションが AffirmativeBased
(Javadoc) を使用している場合、次のように同等の AuthorizationManager
(Javadoc) を作成できます。
Java
Kotlin
XML
@Bean
AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
PolicyAuthorizationManager policy = ...;
LocalAuthorizationManager local = ...;
return AuthorizationManagers.anyOf(policy, local);
}
@Bean
fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
val policy: PolicyAuthorizationManager = ...
val local: LocalAuthorizationManager = ...
return AuthorizationManagers.anyOf(policy, local)
}
<bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
factory-method="anyOf">
<constructor-arg>
<util:list>
<bean class="my.PolicyAuthorizationManager"/>
<bean class="my.LocalAuthorizationManager"/>
</util:list>
</constructor-arg>
</bean>
次に、次のように DSL に接続します。
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
// ...
http {
authorizeHttpRequests {
authorize(anyRequest, requestAuthorization)
}
// ...
}
<http authorization-manager-ref="requestAuthorization"/>
|
ConsensusBased
を使っています
フレームワークが提供する ConsensusBased
(Javadoc) に相当するものはありません。その場合、デリゲート AuthorizationManager
のセットを考慮した複合 AuthorizationManager
(Javadoc) を実装してください。
AuthorizationManager
を実装したら、リファレンスマニュアルの詳細に従ってカスタム AuthorizationManager
を追加してください。
カスタム AccessDecisionVoter
を使用しています
クラスを変更して AuthorizationManager
(Javadoc) を実装するか、アダプターを作成する必要があります。
カスタム投票者が何をしているかを知らなければ、汎用ソリューションを推奨することはできません。ただし、例として、SecurityMetadataSource
(Javadoc) と AccessDecisionVoter
(Javadoc) を anyRequest().authenticated()
に適合させると、次のようになります。
Java
public final class AnyRequestAuthenticatedAuthorizationManagerAdapter implements AuthorizationManager<RequestAuthorizationContext> {
private final SecurityMetadataSource metadata;
private final AccessDecisionVoter voter;
public PreAuthorizeAuthorizationManagerAdapter(SecurityExpressionHandler expressionHandler) {
Map<RequestMatcher, List<ConfigAttribute>> requestMap = Collections.singletonMap(
AnyRequestMatcher.INSTANCE, Collections.singletonList(new SecurityConfig("authenticated")));
this.metadata = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
WebExpressionVoter voter = new WebExpressionVoter();
voter.setExpressionHandler(expressionHandler);
this.voter = voter;
}
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
List<ConfigAttribute> attributes = this.metadata.getAttributes(context);
int decision = this.voter.vote(authentication.get(), invocation, attributes);
if (decision == ACCESS_GRANTED) {
return new AuthorizationDecision(true);
}
if (decision == ACCESS_DENIED) {
return new AuthorizationDecision(false);
}
return null; // abstain
}
}
AuthorizationManager
を実装したら、リファレンスマニュアルの詳細に従ってカスタム AuthorizationManager
を追加してください。
GrantedAuthorityDefaults
を使用する場合は、hasRole
を hasAuthority
に置き換えます。
現在、authorizeHttpRequests
内の hasRole
メソッドは、authorizeRequests
のような GrantedAuthorityDefaults
Bean をサポートしていません。GrantedAuthorityDefaults
を使用してロールのプレフィックスを変更する場合は、hasRole
の代わりに hasAuthority
を使用する必要があります。
例: 以下から変更する必要があります:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.anyRequest().hasRole("ADMIN")
);
return http.build();
}
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
to:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().hasAuthority("MYPREFIX_ADMIN")
);
return http.build();
}
これは将来的にサポートされる予定です。詳細については、gh-13227 [GitHub] (英語) を参照してください。
オプトアウトの手順
問題が発生した場合は、最適なオプトアウト動作について次のシナリオを参照してください。
すべてのディスパッチャー型を保護することはできません
すべてのディスパッチャー型を保護できない場合は、最初に次のように、認可を必要としないディスパッチャー型を宣言してみてください。
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(true)
.dispatcherTypeMatchers(FORWARD, INCLUDE).permitAll()
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = true
authorize(DispatcherTypeRequestMatcher(FORWARD, INCLUDE), permitAll)
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
<intercept-url request-matcher-ref="dispatchers"/>
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
<bean id="dispatchers" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
<constructor-arg>
<util:list value-type="javax.servlet.DispatcherType">
<value>FORWARD</value>
<value>INCLUDE</value>
</util:list>
</constructor-arg>
</bean>
または、それが機能しない場合は、filter-all-dispatcher-types
および filterAllDispatcherTypes
を false
に設定することで、明示的に動作をオプトアウトできます。
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize
.filterAllDispatcherTypes(false)
.mvcMatchers("/app/**").hasRole("APP")
// ...
)
// ...
http {
authorizeHttpRequests {
filterAllDispatcherTypes = false
authorize("/messages/**", hasRole("APP"))
// ...
}
}
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>
または、まだ authorizeRequests
または use-authorization-manager="false"
を使用している場合は、oncePerRequest
を true
に設定します。
Java
Kotlin
XML
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
)
// ...
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/messages/**", hasRole("APP"))
// ...
}
}
<http once-per-request="true" use-authorization-manager="false">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>
すべてのリクエストに対して認可ルールを宣言することはできません
denyAll
の anyRequest
認可ルールの設定に問題がある場合は、代わりに permitAll
(Javadoc) を次のように使用してください。
Java
Kotlin
XML
http
.authorizeHttpReqeusts((authorize) -> authorize
.mvcMatchers("/app/*").hasRole("APP")
// ...
.anyRequest().permitAll()
)
http {
authorizeHttpRequests {
authorize("/app*", hasRole("APP"))
// ...
authorize(anyRequest, permitAll)
}
}
<http>
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="permitAll"/>
</http>
SpEL または AccessDecisionManager
を移行できません
SpEL や AccessDecisionManager
に問題がある場合、または <http>
または authorizeRequests
で使用し続ける必要がある他の機能がある場合は、次のことを試してください。
まず、まだ authorizeRequests
が必要な場合は、引き続き使用してください。非推奨ですが、6.0 では削除されていません。
次に、カスタム access-decision-manager-ref
がまだ必要な場合、または AuthorizationManager
をオプトアウトするその他の理由がある場合は、次のようにします。
<http use-authorization-manager="false">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>