对于最新的稳定版本,请使用 Spring Security 6.4.1! |
使用 AuthorizationFilter 授权 HttpServletRequests
本节以 Servlet 体系结构和实现为基础,深入探讨了授权在基于 Servlet 的应用程序中的工作原理。
AuthorizationFilter 取代FilterSecurityInterceptor .
为了保持向后兼容,FilterSecurityInterceptor 仍然是默认值。
本节讨论如何AuthorizationFilter 有效以及如何覆盖默认配置。 |
这AuthorizationFilter
提供HttpServletRequest
s.
它作为 Security Filters 之一插入到 FilterChainProxy 中。
您可以在声明SecurityFilterChain
.
而不是使用authorizeRequests
用authorizeHttpRequests
这样:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated();
)
// ...
return http.build();
}
这改进了authorizeRequests
以多种方式:
-
使用简化的
AuthorizationManager
API 而不是元数据源、配置属性、决策管理器和投票者。 这简化了重用和定制。 -
延误
Authentication
查找。 它不需要为每个请求查找身份验证,而只会在授权决策需要身份验证的请求中查找身份验证。 -
基于 Bean 的配置支持。
什么时候authorizeHttpRequests
替换为authorizeRequests
然后AuthorizationFilter
替换为FilterSecurityInterceptor
.

-
首先,
AuthorizationFilter
从SecurityContextHolder获取Authentication。 它将其包装在Supplier
以延迟查找。 -
其次,它将
Supplier<Authentication>
和HttpServletRequest
到AuthorizationManager
.-
如果授权被拒绝,则
AccessDeniedException
被抛出。 在这种情况下,ExceptionTranslationFilter
处理AccessDeniedException
. -
如果授予访问权限,
AuthorizationFilter
继续使用 FilterChain,它允许应用程序正常处理。
-
我们可以通过按优先顺序添加更多规则来将 Spring Security 配置为具有不同的规则。
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
// ...
.authorizeHttpRequests(authorize -> authorize (1)
.requestMatchers("/resources/**", "/signup", "/about").permitAll() (2)
.requestMatchers("/admin/**").hasRole("ADMIN") (3)
.requestMatchers("/db/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasRole('DBA')")) (4)
// .requestMatchers("/db/**").access(AuthorizationManagers.allOf(AuthorityAuthorizationManager.hasRole("ADMIN"), AuthorityAuthorizationManager.hasRole("DBA"))) (5)
.anyRequest().denyAll() (6)
);
return http.build();
}
1 | 指定了多个授权规则。 每个规则都按照其声明的顺序进行考虑。 |
2 | 我们指定了任何用户都可以访问的多个 URL 模式。 具体而言,如果 URL 以“/resources/”开头、等于“/signup”或等于“/about”,则任何用户都可以访问请求。 |
3 | 任何以 “/admin/” 开头的 URL 都将被限制为具有 “ROLE_ADMIN” 角色的用户。
您会注意到,由于我们正在调用hasRole 方法,则不需要指定 “ROLE_” 前缀。 |
4 | 任何以 “/db/” 开头的 URL 都要求用户同时具有 “ROLE_ADMIN” 和 “ROLE_DBA”。
您会注意到,由于我们使用的是hasRole 表达式,则不需要指定 “ROLE_” 前缀。 |
5 | 4 中的相同规则,可以通过组合多个来编写AuthorizationManager . |
6 | 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。 |
您可以通过构建自己的方法来采用基于 bean 的方法RequestMatcherDelegatingAuthorizationManager
这样:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> access)
throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().access(access)
)
// ...
return http.build();
}
@Bean
AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationManager(HandlerMappingIntrospector introspector) {
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
RequestMatcher permitAll =
new AndRequestMatcher(
mvcMatcherBuilder.pattern("/resources/**"),
mvcMatcherBuilder.pattern("/signup"),
mvcMatcherBuilder.pattern("/about"));
RequestMatcher admin = mvcMatcherBuilder.pattern("/admin/**");
RequestMatcher db = mvcMatcherBuilder.pattern("/db/**");
RequestMatcher any = AnyRequestMatcher.INSTANCE;
AuthorizationManager<HttpServletRequest> manager = RequestMatcherDelegatingAuthorizationManager.builder()
.add(permitAll, (context) -> new AuthorizationDecision(true))
.add(admin, AuthorityAuthorizationManager.hasRole("ADMIN"))
.add(db, AuthorityAuthorizationManager.hasRole("DBA"))
.add(any, new AuthenticatedAuthorizationManager())
.build();
return (context) -> manager.check(context.getRequest());
}
您还可以为任何请求匹配器连接自己的自定义授权管理器。
以下是将自定义授权管理器映射到my/authorized/endpoint
:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/my/authorized/endpoint").access(new CustomAuthorizationManager());
)
// ...
return http.build();
}
或者你可以为所有请求提供它,如下所示:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().access(new CustomAuthorizationManager());
)
// ...
return http.build();
}
默认情况下,AuthorizationFilter
适用于所有 Dispatcher 类型。
我们可以将 Spring Security 配置为不将授权规则应用于所有调度程序类型,方法是使用shouldFilterAllDispatcherTypes
方法:
-
Java
-
Kotlin
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(false)
.anyRequest().authenticated()
)
// ...
return http.build();
}
@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = false
authorize(anyRequest, authenticated)
}
}
return http.build()
}
而不是设置shouldFilterAllDispatcherTypes
自false
,建议的方法是自定义 Dispatcher 类型的授权。
例如,您可能希望授予对 dispatcher 类型为ASYNC
或FORWARD
.
-
Java
-
Kotlin
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.dispatcherTypeMatchers(DispatcherType.ASYNC, DispatcherType.FORWARD).permitAll()
.anyRequest().authenticated()
)
// ...
return http.build();
}
@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(DispatcherTypeRequestMatcher(DispatcherType.ASYNC, DispatcherType.FORWARD), permitAll)
authorize(anyRequest, authenticated)
}
}
return http.build()
}
您还可以对其进行自定义,以要求调度程序类型的特定角色:
-
Java
-
Kotlin
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.dispatcherTypeMatchers(DispatcherType.ERROR).hasRole("ADMIN")
.anyRequest().authenticated()
)
// ...
return http.build();
}
@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(DispatcherTypeRequestMatcher(DispatcherType.ERROR), hasRole("ADMIN"))
authorize(anyRequest, authenticated)
}
}
return http.build()
}
请求匹配器
这RequestMatcher
interface 用于确定请求是否与给定规则匹配。
我们使用securityMatchers
来确定给定的HttpSecurity
应应用于给定请求。
同样,我们可以使用requestMatchers
来确定我们应该应用于给定请求的授权规则。
请看下面的例子:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**") (1)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/user/**").hasRole("USER") (2)
.requestMatchers("/admin/**").hasRole("ADMIN") (3)
.anyRequest().authenticated() (4)
)
.formLogin(withDefaults());
return http.build();
}
}
@Configuration
@EnableWebSecurity
open class SecurityConfig {
@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/api/**") (1)
authorizeHttpRequests {
authorize("/user/**", hasRole("USER")) (2)
authorize("/admin/**", hasRole("ADMIN")) (3)
authorize(anyRequest, authenticated) (4)
}
}
return http.build()
}
}
1 | 配置HttpSecurity 仅应用于以/api/ |
2 | 允许访问以/user/ 对于具有USER 角色 |
3 | 允许访问以/admin/ 对于具有ADMIN 角色 |
4 | 与上述规则不匹配的任何其他请求都需要身份验证 |
这securityMatcher(s)
和requestMatcher(s)
方法将决定哪些RequestMatcher
implementation 最适合您的应用程序:如果 Spring MVC 在 Classpath 中,则MvcRequestMatcher
,否则,AntPathRequestMatcher
将被使用。
您可以在此处阅读有关 Spring MVC 集成的更多信息。
如果您想使用特定的RequestMatcher
,只需将实现传递给securityMatcher
和/或requestMatcher
方法:
-
Java
-
Kotlin
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; (1)
import static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher(antMatcher("/api/**")) (2)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(antMatcher("/user/**")).hasRole("USER") (3)
.requestMatchers(regexMatcher("/admin/.*")).hasRole("ADMIN") (4)
.requestMatchers(new MyCustomRequestMatcher()).hasRole("SUPERVISOR") (5)
.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
}
public class MyCustomRequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
// ...
}
}
import org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher (1)
import org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher
@Configuration
@EnableWebSecurity
open class SecurityConfig {
@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher(antMatcher("/api/**")) (2)
authorizeHttpRequests {
authorize(antMatcher("/user/**"), hasRole("USER")) (3)
authorize(regexMatcher("/admin/**"), hasRole("ADMIN")) (4)
authorize(MyCustomRequestMatcher(), hasRole("SUPERVISOR")) (5)
authorize(anyRequest, authenticated)
}
}
return http.build()
}
}
1 | 导入静态工厂方法AntPathRequestMatcher 和RegexRequestMatcher 创建RequestMatcher 实例。 |
2 | 配置HttpSecurity 仅应用于以/api/ 用AntPathRequestMatcher |
3 | 允许访问以/user/ 对于具有USER 角色, 使用AntPathRequestMatcher |
4 | 允许访问以/admin/ 对于具有ADMIN 角色, 使用RegexRequestMatcher |
5 | 允许访问与MyCustomRequestMatcher 对于具有SUPERVISOR 角色, 使用自定义RequestMatcher |
表达 式
建议您使用类型安全的授权管理器而不是 SPEL。
然而WebExpressionAuthorizationManager
可用于帮助迁移旧版 SPEL。
要使用WebExpressionAuthorizationManager
,您可以使用尝试迁移的表达式构造一个 Importing,如下所示:
-
Java
-
Kotlin
.requestMatchers("/test/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
.requestMatchers("/test/**").access(WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
如果你在表达式中引用一个 bean,如下所示:@webSecurity.check(authentication, request)
,建议您改为直接调用 Bean,它看起来类似于以下内容:
-
Java
-
Kotlin
.requestMatchers("/test/**").access((authentication, context) ->
new AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))
.requestMatchers("/test/**").access((authentication, context): AuthorizationManager<RequestAuthorizationContext> ->
AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))
对于包含 Bean 引用以及其他表达式的复杂指令,建议您更改这些指令以实现AuthorizationManager
并通过调用.access(AuthorizationManager)
.
如果您无法执行此作,则可以配置DefaultHttpSecurityExpressionHandler
替换为 bean 解析器,并将其提供给WebExpressionAuthorizationManager#setExpressionhandler
.