このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Security 6.5.5 を使用してください! |
WebFlux セキュリティ
Spring Security の WebFlux サポートは WebFilter
に依存しており、Spring WebFlux および Spring WebFlux.Fn でも同じように機能します。いくつかのサンプルアプリケーションがコードを示しています。
Hello WebFlux hellowebflux [GitHub] (英語)
Hello WebFlux.Fn hellowebfluxfn [GitHub] (英語)
Hello WebFlux メソッド hellowebflux-method [GitHub] (英語)
最小限の WebFlux セキュリティ構成
次のリストは、最小限の WebFlux セキュリティ構成を示しています。
Java
Kotlin
@Configuration
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
@Configuration
@EnableWebFluxSecurity
class HelloWebfluxSecurityConfig {
@Bean
fun userDetailsService(): ReactiveUserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(userDetails)
}
}
この構成は、フォームと HTTP の基本認証を提供し、任意のページにアクセスするために認証されたユーザーを要求する認可を設定し、デフォルトのログインページとデフォルトのログアウトページを設定し、セキュリティ関連の HTTP ヘッダーを設定し、CSRF 保護を追加します。
明示的な WebFlux セキュリティ構成
次のページは、最小限の WebFlux セキュリティ構成の明示的なバージョンを示しています。
Java
Kotlin
@Configuration
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.httpBasic(withDefaults())
.formLogin(withDefaults());
return http.build();
}
}
import org.springframework.security.config.web.server.invoke
@Configuration
@EnableWebFluxSecurity
class HelloWebfluxSecurityConfig {
@Bean
fun userDetailsService(): ReactiveUserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(userDetails)
}
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
formLogin { }
httpBasic { }
}
}
}
IDE がメソッドを常に自動インポートするとは限らず、コンパイルの問題が発生する可能性があるため、クラスで Kotlin DSL を有効にするには、必ず org.springframework.security.config.web.server.invoke 関数をインポートしてください。 |
この構成は、最小構成と同じものをすべて明示的に設定します。ここから、デフォルトをより簡単に変更できます。
config/src/test/
ディレクトリの EnableWebFluxSecurity
[GitHub] (英語) を検索すると、単体テストで明示的な構成の例をさらに見つけることができます。
複数のチェーンサポート
複数の SecurityWebFilterChain
インスタンスを構成して、RequestMatcher
インスタンスごとに構成を分離できます。
例: /api
で始まる URL の構成を分離できます:
Java
Kotlin
@Configuration
@EnableWebFluxSecurity
static class MultiSecurityHttpConfig {
@Order(Ordered.HIGHEST_PRECEDENCE) (1)
@Bean
SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {
http
.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) (2)
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt); (3)
return http.build();
}
@Bean
SecurityWebFilterChain webHttpSecurity(ServerHttpSecurity http) { (4)
http
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.httpBasic(withDefaults()); (5)
return http.build();
}
@Bean
ReactiveUserDetailsService userDetailsService() {
return new MapReactiveUserDetailsService(
PasswordEncodedUser.user(), PasswordEncodedUser.admin());
}
}
import org.springframework.security.config.web.server.invoke
@Configuration
@EnableWebFluxSecurity
open class MultiSecurityHttpConfig {
@Order(Ordered.HIGHEST_PRECEDENCE) (1)
@Bean
open fun apiHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
securityMatcher(PathPatternParserServerWebExchangeMatcher("/api/**")) (2)
authorizeExchange {
authorize(anyExchange, authenticated)
}
oauth2ResourceServer {
jwt { } (3)
}
}
}
@Bean
open fun webHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain { (4)
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
httpBasic { } (5)
}
}
@Bean
open fun userDetailsService(): ReactiveUserDetailsService {
return MapReactiveUserDetailsService(
PasswordEncodedUser.user(), PasswordEncodedUser.admin()
)
}
}
1 | @Order を使用して SecurityWebFilterChain を構成し、Spring Security が最初に検討する必要がある SecurityWebFilterChain を指定します |
2 | PathPatternParserServerWebExchangeMatcher を使用して、この SecurityWebFilterChain が /api/ で始まる URL パスにのみ適用されることを記述します |
3 | /api/** エンドポイントに使用される認証メカニズムを指定します |
4 | 他のすべての URL と一致するように、優先順位の低い SecurityWebFilterChain の別のインスタンスを作成します |
5 | アプリケーションの残りの部分で使用される認証メカニズムを指定します |
Spring Security は、リクエストごとに 1 つの SecurityWebFilterChain
@Bean
を選択します。securityMatcher
定義の順番でリクエストに一致します。
この場合、URL パスが /api
で始まる場合、Spring Security は apiHttpSecurity
を使用することを意味します。URL が /api
で始まらない場合、Spring Security はデフォルトで webHttpSecurity
になります。これには、任意のリクエストに一致する暗黙の securityMatcher
があります。
モジュラー ServerHttpSecurity 構成
多くのユーザーは、Spring Security の設定を一元管理することを好み、SecurityWebFilterChain
Bean 宣言内で設定することを選択します。しかし、設定をモジュール化したい場合もあります。これは、以下の方法で実現できます。
カスタマイザー <ServerHttpSecurity> Bean
セキュリティ構成をモジュール化したい場合は、Customizer<ServerHttpSecurity>
Bean にロジックを配置できます。例: 次の構成では、すべての ServerHttpSecurity
インスタンスが次のように構成されます。
Java
Kotlin
@Bean
Customizer<ServerHttpSecurity> httpSecurityCustomizer() {
return (http) -> http
.headers((headers) -> headers
.contentSecurityPolicy((csp) -> csp
(1)
.policyDirectives("object-src 'none'")
)
)
(2)
.redirectToHttps(Customizer.withDefaults());
}
@Bean
fun httpSecurityCustomizer(): Customizer<ServerHttpSecurity> {
return Customizer { http -> http
.headers { headers -> headers
.contentSecurityPolicy { csp -> csp
(1)
.policyDirectives("object-src 'none'")
}
}
(2)
.redirectToHttps(Customizer.withDefaults())
}
}
1 | コンテンツセキュリティポリシーを object-src 'none' に設定する |
2 | すべてのリクエストを https にリダイレクトする |
トップレベル ServerHttpSecurity カスタマイザー Bean
セキュリティ構成をさらにモジュール化したい場合は、Spring Security によってトップレベルの HttpSecurity
および Customizer
Bean が自動的に適用されます。
最上位の HttpSecurity
型または Customizer
型は、public HttpSecurity.*(Customizer<T>)
に一致する任意の Customizer<T>
として要約できます。これは、HttpSecurity
(Javadoc) の public メソッドの単一引数である任意の Customizer<T>
に相当します。
A few examples can help to clarify. If Customizer<ContentTypeOptionsConfig>
is published as a Bean, it will not be be automatically applied because it is an argument to HeadersConfigurer.contentTypeOptions(Customizer)
(Javadoc) which is not a method defined on HttpSecurity
. However, if Customizer<HeadersConfigurer<HttpSecurity>>
is published as a Bean, it will be automatically applied because it is an argument to HttpSecurity.headers(Customizer)
(Javadoc) 。
For example, the following configuration will ensure that the コンテンツセキュリティポリシー is set to object-src 'none'
:
Java
Kotlin
@Bean
Customizer<ServerHttpSecurity.HeaderSpec> headersSecurity() {
return (headers) -> headers
.contentSecurityPolicy((csp) -> csp
(1)
.policyDirectives("object-src 'none'")
);
}
@Bean
fun headersSecurity(): Customizer<ServerHttpSecurity.HeaderSpec> {
return Customizer { headers -> headers
.contentSecurityPolicy { csp -> csp
(1)
.policyDirectives("object-src 'none'")
}
}
}
Customizer Bean Ordering
First each Customizer<HttpSecurity> Bean is applied using ObjectProvider#orderedStream() (Javadoc) . This means that if there are multiple Customizer<HttpSecurity>
Beans, the @Order (Javadoc) annotation can be added to the Bean definitions to control the ordering.
Next every トップレベル HttpSecurity カスタマイザー Bean type is looked up and each is is applied using ObjectProvider#orderedStream()
. If there is are two Customizer<HeadersConfigurer<HttpSecurity>>
beans and two Customizer<HttpsRedirectConfigurer<HttpSecurity>>
instances, the order that each Customizer
type is invoked is undefined. However, the order that each instance of Customizer<HttpsRedirectConfigurer<HttpSecurity>>
is defined by ObjectProvider#orderedStream()
and can be controlled using @Order
on the Bean the definitions.
Finally, the HttpSecurity
Bean is injected as a Bean. All Customizer
instances are applied before the HttpSecurity
Bean is created. This allows overriding the customizations provided by the Customizer
Beans.
You can find an example below that illustrates the ordering:
Java
Kotlin
@Bean (4)
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
http
.authorizeExchange((exchange) -> exchange
.anyExchange().authenticated()
);
return http.build();
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) (2)
Customizer<ServerHttpSecurity> userAuthorization() {
return (http) -> http
.authorizeExchange((exchange) -> exchange
.pathMatchers("/users/**").hasRole("USER")
);
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) (1)
Customizer<ServerHttpSecurity> adminAuthorization() {
return (http) -> http
.authorizeExchange((exchange) -> exchange
.pathMatchers("/admins/**").hasRole("ADMIN")
);
}
(3)
@Bean
Customizer<ServerHttpSecurity.HeaderSpec> contentSecurityPolicy() {
return (headers) -> headers
.contentSecurityPolicy((csp) -> csp
.policyDirectives("object-src 'none'")
);
}
@Bean
Customizer<ServerHttpSecurity.HeaderSpec> contentTypeOptions() {
return (headers) -> headers
.contentTypeOptions(Customizer.withDefaults());
}
@Bean
Customizer<ServerHttpSecurity.HttpsRedirectSpec> httpsRedirect() {
return Customizer.withDefaults();
}
@Bean (4)
fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
http
.authorizeExchange({ exchanges -> exchanges
.anyExchange().authenticated()
})
return http.build()
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) (2)
fun userAuthorization(): Customizer<ServerHttpSecurity> {
return Customizer { http -> http
.authorizeExchange { exchanges -> exchanges
.pathMatchers("/users/**").hasRole("USER")
}
}
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) (1)
fun adminAuthorization(): Customizer<ServerHttpSecurity> {
return ThrowingCustomizer { http -> http
.authorizeExchange { exchanges -> exchanges
.pathMatchers("/admins/**").hasRole("ADMIN")
}
}
}
(3)
@Bean
fun contentSecurityPolicy(): Customizer<ServerHttpSecurity.HeaderSpec> {
return Customizer { headers -> headers
.contentSecurityPolicy { csp -> csp
.policyDirectives("object-src 'none'")
}
}
}
@Bean
fun contentTypeOptions(): Customizer<ServerHttpSecurity.HeaderSpec> {
return Customizer { headers -> headers
.contentTypeOptions(Customizer.withDefaults())
}
}
@Bean
fun httpsRedirect(): Customizer<ServerHttpSecurity.HttpsRedirectSpec> {
return Customizer.withDefaults()
}
1 | First all Customizer<HttpSecurity> instances are applied. The adminAuthorization Bean has the highest @Order so it is applied first. If there are no @Order annotations on the Customizer<HttpSecurity> Beans or the @Order annotations had the same value, then the order that the Customizer<HttpSecurity> instances are applied is undefined. |
2 | The userAuthorization is applied next due to being an instance of Customizer<HttpSecurity> |
3 | The order that the Customizer types are undefined. In this example, the order of contentSecurityPolicy 、contentTypeOptions 、httpsRedirect are undefined. If @Order(Ordered.HIGHEST_PRECEDENCE) was added to contentTypeOptions , then we would know that contentTypeOptions is before contentSecurityPolicy (they are the same type), but we do not know if httpsRedirect is before or after the Customizer<HeadersConfigurer<HttpSecurity>> Beans. |
4 | After all of the Customizer Beans are applied, the HttpSecurity is passed in as a Bean. |