此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Security 6.5.3spring-doc.cadn.net.cn

WebFlux 安全

Spring Security 的 WebFlux 支持依赖于WebFilter并且对 Spring WebFlux 和 Spring WebFlux.Fn 的工作原理相同。 一些示例应用程序演示了代码:spring-doc.cadn.net.cn

最小的 WebFlux 安全配置

以下列表显示了最低的 WebFlux Security 配置:spring-doc.cadn.net.cn

最小的 WebFlux 安全配置
@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 保护等。spring-doc.cadn.net.cn

显式 WebFlux 安全配置

以下页面显示了最小 WebFlux 安全配置的显式版本:spring-doc.cadn.net.cn

显式 WebFlux 安全配置
@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 { }
        }
    }
}
确保导入org.springframework.security.config.web.server.invoke函数在您的类中启用 Kotlin DSL,因为 IDE 不会总是自动导入该方法,从而导致编译问题。

此配置显式设置了与我们的最小配置相同的所有内容。 从这里,您可以更轻松地更改默认值。spring-doc.cadn.net.cn

您可以在单元测试中找到更多显式配置示例,方法是搜索EnableWebFluxSecurityconfig/src/test/目录.spring-doc.cadn.net.cn

多链支持

您可以配置多个SecurityWebFilterChain实例,以分隔配置RequestMatcher实例。spring-doc.cadn.net.cn

例如,您可以隔离以/api:spring-doc.cadn.net.cn

@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 配置一个SecurityWebFilterChain使用@Order指定哪个SecurityWebFilterChainSpring Security 应首先考虑
2 PathPatternParserServerWebExchangeMatcher声明这个SecurityWebFilterChain仅适用于以/api/
3 指定将用于/api/**端点
4 创建另一个SecurityWebFilterChain优先级较低,以匹配所有其他网址
5 指定将用于应用程序其余部分的身份验证机制

Spring Security 选择一个SecurityWebFilterChain @Bean对于每个请求。 它按securityMatcher定义。spring-doc.cadn.net.cn

在这种情况下,这意味着,如果 URL 路径以/api,Spring Security 使用apiHttpSecurity. 如果 URL 不是以/api,Spring Security 默认为webHttpSecurity,其中隐含的securityMatcher与任何请求匹配。spring-doc.cadn.net.cn

模块化服务器Http安全配置

许多用户更喜欢他们的 Spring Security 配置位于集中位置,并选择在SecurityWebFilterChainBean 声明。 但是,有时用户可能希望将配置模块化。 这可以使用以下方法完成:spring-doc.cadn.net.cn

Customizer<ServerHttpSecurity> Bean

如果您想模块化安全配置,可以将逻辑放在Customizer<ServerHttpSecurity>豆。 例如,以下配置将确保所有ServerHttpSecurity实例配置为:spring-doc.cadn.net.cn

@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

顶级服务器HttpSecurity 定制器 Bean

如果您希望进一步模块化安全配置,Spring Security 将自动应用任何顶级HttpSecurity Customizer豆。spring-doc.cadn.net.cn

顶级HttpSecurity Customizer类型可以概括为Customizer<T>匹配public HttpSecurity.*(Customizer<T>). 这转化为任何Customizer<T>这是公共方法的单个参数HttpSecurity.spring-doc.cadn.net.cn

几个例子可以帮助澄清。 如果Customizer<ContentTypeOptionsConfig>作为 Bean 发布,它不会自动应用,因为它是HeadersConfigurer.contentTypeOptions(Customizer)这不是在HttpSecurity. 但是,如果Customizer<HeadersConfigurer<HttpSecurity>>作为 Bean 发布,它将自动应用,因为它是HttpSecurity.headers(Customizer).spring-doc.cadn.net.cn

例如,以下配置将确保将内容安全策略设置为object-src 'none':spring-doc.cadn.net.cn

@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'")
        }
    }
}

定制器豆子订购

首先,使用 ObjectProvider#orderedStream() 应用每个 Customizer<HttpSecurity> Bean。 这意味着,如果有多个Customizer<HttpSecurity>Bean,可以将@Order注释添加到 Bean 定义中以控制排序。spring-doc.cadn.net.cn

接下来,查找每个顶级 HttpSecurity Customizer Bean 类型,并使用ObjectProvider#orderedStream(). 如果有两个Customizer<HeadersConfigurer<HttpSecurity>>豆子和两个Customizer<HttpsRedirectConfigurer<HttpSecurity>>实例,每个Customizertype 被调用为 undefined。 但是,每个实例的顺序Customizer<HttpsRedirectConfigurer<HttpSecurity>>ObjectProvider#orderedStream()并且可以使用@Order在 Bean 上定义。spring-doc.cadn.net.cn

最后,HttpSecurityBean 作为 Bean 注入。 都Customizer实例在HttpSecurityBean 被创建。 这允许覆盖Customizer豆。spring-doc.cadn.net.cn

您可以在下面找到一个示例来说明排序:spring-doc.cadn.net.cn

@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 首先Customizer<HttpSecurity>实例。 这adminAuthorization豆子的@Order所以首先应用它。 如果没有@Order注释Customizer<HttpSecurity>Beans 或@Order注释具有相同的值,则Customizer<HttpSecurity>实例被应用为未定义。
2 userAuthorization由于是Customizer<HttpSecurity>
3 命令Customizer类型未定义。 在此示例中,的顺序contentSecurityPolicy,contentTypeOptionshttpsRedirect是未定义的。 如果@Order(Ordered.HIGHEST_PRECEDENCE)被添加到contentTypeOptions,那么我们就会知道contentTypeOptionscontentSecurityPolicy(它们是同一类型),但我们不知道是否httpsRedirectCustomizer<HeadersConfigurer<HttpSecurity>>豆。
4 毕竟Customizer应用 bean,则HttpSecurity作为 Bean 传入。