Authorize HttpServletRequests
SpringSecurity 允许您在请求级别对授权进行建模。例如,对于 Spring Security,可以说/admin 下的所有页面都需要一个权限,而其他所有页面只需要身份验证。
默认情况下,SpringSecurity 要求对每个请求进行身份验证。也就是说,任何时候使用 HttpSecurity 实例,都需要声明授权规则。
无论何时有 HttpSecurity 实例,您至少应该这样做:
Use authorizeHttpRequests
java
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
这告诉 Spring Security,应用程序中的任何端点都需要至少对安全上下文进行身份验证才能允许它。
在许多情况下,您的授权规则将比这更复杂,因此请考虑以下用例:
- 我有一个使用 AuthorizeRequest 的应用程序,我想将它迁移到 AuthorizeHttpRequest(I have an app that uses
authorizeRequests
and I want to migrate it toauthorizeHttpRequests)
- 我想了解 AuthorizationFilter 组件是如何工作的(I want to understand how the
AuthorizationFilter
components work) - 我希望根据模式匹配请求,特别是正则表达式(I want to match requests based on a pattern; specifically regex)
- 我想匹配请求,并将 SpringMVC 映射到默认 servlet 之外的其他内容(I want to match request, and I map Spring MVC to something other than the default servlet)
- I want to authorize requests
- 希望以编程方式匹配请求(I want to match a request programmatically)
- 我想以编程方式授权一个请求(I want to authorize a request programmatically)
- 我要将请求授权委托给策略代理(I want to delegate request authorization to a policy agent)
Understanding How Request Authorization Components Work
本节通过深入研究基于 Servlet 的应用程序中的请求级授权如何工作,构建在 Servlet 体系结构和实现的基础上。
- 首先,AuthorizationFilter 构建了一个 Supplier,用于从 SecurityContextHolder 检索 Authentication。(First, the
AuthorizationFilter
constructs aSupplier
that retrieves an Authentication from the SecurityContextHolder.) - 第二,它将 Supplier 和 HttpServletRequest 传递给 AuthorizationManager。AuthorizationManager 将请求与 authorizeHttpRequests 中的模式进行匹配,并运行相应的规则。
- 如果授权被拒绝,将发布一个 AuthorizationDeniedEvent,并抛出一个 AccessDeniedException。在这种情况下,ExceptionTranslationFilter 将处理 AccessDeniedException。
- 如果授予访问权限,则发布 AuthorizationGrantedEvent,AuthorizationFilter 继续使用 FilterChain,它允许应用程序正常处理。
AuthorizationFilter
Is Last By Default
AuthorizationFilter 默认是 Spring Security 过滤器链中的最后一个。这意味着 Spring Security 的认证过滤器、漏洞保护以及其他过滤器集成不需要授权。如果您在 AuthorizationFilter 之前添加自己的过滤器,它们也不需要授权;否则,它们将需要授权。
一个典型的场景是当您添加 Spring MVC 端点时。因为它们由 DispatcherServlet 执行,这发生在 AuthorizationFilter 之后,所以您的端点需要被包含在 authorizeHttpRequests 中才能被允许。
All Dispatches Are Authorized
AuthorizationFilter 不仅在每次请求时运行,而且在每次分发时都会运行。这意味着 REQUEST 分发需要授权,但 FORWARD、ERROR 和 INCLUDE 也是如此。
例如,Spring MVC 可以将请求转发到呈现 Thymeleaf 模板的视图解析器,如下所示:
java
@Controller
public class MyController {
@GetMapping("/endpoint")
public String endpoint() {
return "endpoint";
}
}
在这种情况下,授权发生两次; 一次用于授权/端点,一次用于转发到 Thymeleaf 以呈现"端点"模板。
因此,您可能希望允许所有 FORWARD 分派。
这个原则的另一个例子是 SpringBoot 如何处理错误。如果容器捕获到异常,则如下所示:
Sample Erroring Spring MVC Controller
java
@Controller
public class MyController {
@GetMapping("/endpoint")
public String endpoint() {
throw new UnsupportedOperationException("unsupported");
}
}
然后 Boot 会将其发送到 ERROR 调度。
在这种情况下,授权也会发生两次: 一次用于授权/端点,一次用于分派错误。
因此,您可能希望允许所有 ERROR 分派。
Authentication
Lookup is Deferred
记住 AuthorizationManager API 使用 Supplier < Authentication > 。
当请求总是被允许或总是被拒绝时,这与 AuthorizeHttpRequest 有关。在这些情况下,不会查询身份验证,从而使请求速度更快。
Authorizing an Endpoint
通过在优先顺序中添加更多规则,可以将 Spring Security 配置为具有不同的规则。
如果您希望只有具有 USER 权限的最终用户才能访问该/端点,那么您可以这样做:
java
@Bean
public SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/endpoint").hasAuthority("USER")
.anyRequest().authenticated()
)
// ...
return http.build();
}
如您所见,声明可以分解为pattern/rule对。
AuthorizationFilter 按列出的顺序处理这些对,只将第一个匹配应用到请求上。这意味着,即使 /** 也会匹配 /endpoint,上述规则也不是问题。阅读上述规则的方式是:"如果请求是 /endpoint,则需要 USER 权限;否则,只需认证"。
SpringSecurity 支持多种模式和多种规则; 您还可以通过编程方式创建自己的模式和规则。
一旦授权,您可以使用 Security 的测试支持以下方式对其进行测试:
Test Endpoint Authorization
java
@WithMockUser(authorities="USER")
@Test
void endpointWhenUserAuthorityThenAuthorized() {
this.mvc.perform(get("/endpoint"))
.andExpect(status().isOk());
}
@WithMockUser
@Test
void endpointWhenNotUserAuthorityThenForbidden() {
this.mvc.perform(get("/endpoint"))
.andExpect(status().isForbidden());
}
@Test
void anyWhenUnauthenticatedThenUnauthorized() {
this.mvc.perform(get("/any"))
.andExpect(status().isUnauthorized());
}
Matching Requests
上面已经介绍了两种匹配请求的方法。
您看到的第一个是最简单的,即匹配任何请求。
第二种是通过 URI 模式进行匹配。Spring Security 支持两种用于 URI 模式匹配的语言: Ant (如上所示)和正则表达式。
Matching Using Ant
Ant 是 Spring Security 用来匹配请求的默认语言。
您可以使用它来匹配单个端点或目录,甚至可以捕获占位符以供以后使用。您还可以对其进行细化以匹配一组特定的 HTTP 方法。
假设您不希望匹配/endpoint 端点,而是希望匹配/resource 目录下的所有端点。在这种情况下,您可以执行以下操作:
Match with Ant
java
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/resource/**").hasAuthority("USER")
.anyRequest().authenticated()
)
解读方法是"如果请求是/resource 或某个子目录,则需要 USER 权限; 否则,只需要身份验证"
您还可以从请求中提取路径值,如下所示:
Authorize and Extract
java
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/resource/{name}").access(new WebExpressionAuthorizationManager("#name == authentication.name"))
.anyRequest().authenticated()
)
一旦授权,您可以使用 Security 的测试支持以下方式对其进行测试:
Test Directory Authorization
java
@WithMockUser(authorities="USER")
@Test
void endpointWhenUserAuthorityThenAuthorized() {
this.mvc.perform(get("/endpoint/jon"))
.andExpect(status().isOk());
}
@WithMockUser
@Test
void endpointWhenNotUserAuthorityThenForbidden() {
this.mvc.perform(get("/endpoint/jon"))
.andExpect(status().isForbidden());
}
@Test
void anyWhenUnauthenticatedThenUnauthorized() {
this.mvc.perform(get("/any"))
.andExpect(status().isUnauthorized());
}
SpringSecurity 只匹配路径。如果要匹配查询参数,则需要一个自定义请求匹配器。
Matching Using Regular Expressions
SpringSecurity 支持将请求与正则表达式匹配。如果您想在子目录上应用比 * * 更严格的匹配条件,这可能很方便。
例如,考虑一个包含用户名的路径和所有用户名必须是字母数字的规则。您可以使用 RegexRequestMatcher 来遵守此规则,如下所示:
java
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(RegexRequestMatcher.regexMatcher("/resource/[A-Za-z0-9]+")).hasAuthority("USER")
.anyRequest().denyAll()
)
Matching By Http Method
还可以通过 HTTP 方法匹配规则。有一个地方很方便,那就是通过授予的权限进行授权,比如授予读或写权限。
为了要求所有 GET 都具有读权限,并且所有 POST 都具有写权限,您可以这样做:
Match by HTTP Method
java
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(HttpMethod.GET).hasAuthority("read")
.requestMatchers(HttpMethod.POST).hasAuthority("write")
.anyRequest().denyAll()
)
这些授权规则的内容应该是: "如果请求是 GET,则需要读权限; 否则,如果请求是 POST,则需要写权限; 否则,拒绝请求"
默认情况下拒绝请求是一种健康的安全实践,因为它将规则集转换为允许列表。
一旦授权,您可以使用 Security 的测试支持以下方式对其进行测试:
Test Http Method Authorization
java
@WithMockUser(authorities="read")
@Test
void getWhenReadAuthorityThenAuthorized() {
this.mvc.perform(get("/any"))
.andExpect(status().isOk());
}
@WithMockUser
@Test
void getWhenNoReadAuthorityThenForbidden() {
this.mvc.perform(get("/any"))
.andExpect(status().isForbidden());
}
@WithMockUser(authorities="write")
@Test
void postWhenWriteAuthorityThenAuthorized() {
this.mvc.perform(post("/any").with(csrf()))
.andExpect(status().isOk());
}
@WithMockUser(authorities="read")
@Test
void postWhenNoWriteAuthorityThenForbidden() {
this.mvc.perform(get("/any").with(csrf()))
.andExpect(status().isForbidden());
}
Matching By Dispatcher Type
XML 当前不支持此特性
如前所述,SpringSecurity 默认授权所有调度程序类型。而且,即使在 REQUEST 分派上建立的安全上下文将传递给后续分派,微妙的不匹配有时也会导致意外的 AccessDeniedException。
为了解决这个问题,您可以配置 Spring Security Java 配置,以允许 FORWARD 和 ERROR 等调度程序类型,如下所示:
java
http
.authorizeHttpRequests((authorize) -> authorize
.dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
.requestMatchers("/endpoint").permitAll()
.anyRequest().denyAll()
)
Using an MvcRequestMatcher
一般来说,可以像上面演示的那样使用 requestMatcher (String)。
但是,如果将 SpringMVC 映射到不同的 servlet 路径,则需要在安全配置中考虑这一点。
例如,如果 Spring MVC 被映射到/Spring-MVC 而不是/(默认值) ,那么您可能有一个想要授权的端点,比如/Spring-MVC/my/controller。
您需要使用 MvcRequestMatcher 在配置中分割 servlet 路径和控制器路径,如下所示:
Example 2. Match by MvcRequestMatcher
java
@Bean
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector).servletPath("/spring-mvc");
}
@Bean
SecurityFilterChain appEndpoints(HttpSecurity http, MvcRequestMatcher.Builder mvc) {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(mvc.pattern("/my/controller/**")).hasAuthority("controller")
.anyRequest().authenticated()
);
return http.build();
}
这种需要至少可以通过两种不同的方式产生:
- 如果使用 spring.mvc.servlet.path Boot 属性将默认路径(/)更改为其他内容
- 如果您注册了多个 Spring MVC DispatcherServlet (因此要求其中一个不是默认路径)
Using a Custom Matcher
XML 当前不支持此特性
在 Java 配置中,您可以创建自己的 RequestMatcher 并将其提供给 DSL,如下所示:
Example 3. Authorize by Dispatcher Type
java
RequestMatcher printview = (request) -> request.getParameter("print") != null;
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(printview).hasAuthority("print")
.anyRequest().authenticated()
)
一旦授权,您可以使用 Security 的测试支持以下方式对其进行测试:
java
@WithMockUser(authorities="print")
@Test
void printWhenPrintAuthorityThenAuthorized() {
this.mvc.perform(get("/any?print"))
.andExpect(status().isOk());
}
@WithMockUser
@Test
void printWhenNoPrintAuthorityThenForbidden() {
this.mvc.perform(get("/any?print"))
.andExpect(status().isForbidden());
}
Authorizing Requests
一旦匹配了一个请求,您可以通过几种方式对其进行授权,如 permitAll、 denyAll 和 hasAuthority。
作为一个快速总结,以下是 DSL 中内置的授权规则:
- PermitAll-请求不需要授权,是一个公共端点; 请注意,在这种情况下,永远不会从会话检索 Authentication
- DenyAll-在任何情况下都不允许请求; 请注意,在这种情况下,永远不会从会话检索 Authentication
- HasAuthority ------该请求要求 Authentication 具有与给定值匹配的 GrantedAuthority
- HasRole-hasAuthority 的一个快捷方式,它将 ROLE _ 或其他配置为默认前缀的内容作为前缀
- HasAnyAuthority ------该请求要求 Authentication 具有与任何给定值匹配的 GrantedAuthority
- HasAnyrole-hasAnyAuthority 的一个快捷方式,它将 ROLE _ 或任何被配置为默认前缀的东西作为前缀
- Access-请求使用此自定义 AuthorizationManager 来确定访问权限
现在已经了解了模式、规则以及如何将它们配对在一起,您应该能够理解在这个更复杂的示例中发生了什么:
java
import static jakarta.servlet.DispatcherType.*;
import static org.springframework.security.authorization.AuthorizationManagers.allOf;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole;
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
// ...
.authorizeHttpRequests(authorize -> authorize (1)
.dispatcherTypeMatchers(FORWARD, ERROR).permitAll() (2)
.requestMatchers("/static/**", "/signup", "/about").permitAll() (3)
.requestMatchers("/admin/**").hasRole("ADMIN") (4)
.requestMatchers("/db/**").access(allOf(hasAuthority("db"), hasRole("ADMIN"))) (5)
.anyRequest().denyAll() (6)
);
return http.build();
}
- 指定了多个授权规则。每个规则按其声明的顺序进行考虑。
- 分派 FORWARD 和 ERROR 允许 Spring MVC 呈现视图,Spring Boot 呈现错误
- 我们指定了任何用户都可以访问的多个 URL 模式。具体来说,如果 URL 以"/static/"、等于"/signup"或等于"/about"开头,则任何用户都可以访问请求。
- 任何以"/admin/"开头的 URL 将仅限于具有" ROLE _ ADMIN"角色的用户。您会注意到,由于我们正在调用 hasRole 方法,因此不需要指定" ROLE _"前缀。
- 任何以 "/db/" 开头的 URL 都需要用户同时拥有 "db" 权限以及 "ROLE_ADMIN" 角色。您会注意到,由于我们使用了 hasRole 表达式,因此不需要指定 "ROLE_" 前缀。
- 任何尚未匹配的 URL 都将被拒绝访问。如果您不想意外地忘记更新授权规则,那么这是一个很好的策略。
Expressing Authorization with SpEL
虽然建议使用具体的 AuthorizationManager,但是在某些情况下需要使用表达式,比如使用 < stop-url > 或 JSP Taglibs。出于这个原因,本节将重点介绍来自这些域的示例。
既然如此,让我们更深入地讨论一下 Spring Security 的 Web 安全授权 SpEL API。
Spring Security 将其所有的授权字段和方法封装在一个集合的根对象中。最通用的根对象称为 SecurityExpressionRoot,它是 WebSecurityExpressionRoot 的基础。当准备评估授权表达式时,Spring Security 将这个根对象提供给 StandardEvaluationContext。
Using Authorization Expression Fields and Methods
它提供的第一件事是增强了 SpEL 表达式的授权字段和方法集。以下是最常用方法的简要概述:
-
permitAll - 请求不需要任何授权即可调用;注意,在这种情况下,Authentication 从未从会话中检索
-
denyAll - 请求在任何情况下都不被允许;注意,在这种情况下,Authentication 从未从会话中检索
-
hasAuthority - 请求需要 Authentication 具有与给定值匹配的 GrantedAuthority
-
hasRole - hasAuthority 的快捷方式,默认前缀为 ROLE_ 或配置的默认前缀
-
hasAnyAuthority - 请求需要 Authentication 具有与给定值中的任何一个匹配的 GrantedAuthority
-
hasAnyRole - hasAnyAuthority 的快捷方式,默认前缀为 ROLE_ 或配置的默认前缀
-
HasPermission-一个连接到您的 Permisonevalator 实例的钩子,用于执行对象级授权
以下是一些最常见的领域:
-
authentication - 与本方法调用相关联的 Authentication 实例
-
principal - 与本方法调用相关联的 Authentication#getPrincipal
现在已经了解了模式、规则以及如何将它们配对在一起,您应该能够理解在这个更复杂的示例中发生了什么:
Authorize Requests Using SpEL
java
<http>
<intercept-url pattern="/static/**" access="permitAll"/> (1)
<intercept-url pattern="/admin/**" access="hasRole('ADMIN')"/>(2)
<intercept-url pattern="/db/**" access="hasAuthority('db') and hasRole('ADMIN')"/>(3)
<intercept-url pattern="/**" access="denyAll"/>(4)
</http>
- 我们指定了一个任何用户都可以访问的 URL 模式。具体来说,如果 URL 以"/static/"开头,任何用户都可以访问请求。
- 任何以"/admin/"开头的 URL 将仅限于具有" ROLE _ ADMIN"角色的用户。您会注意到,由于我们正在调用 hasRole 方法,因此不需要指定" ROLE _"前缀。
- 任何以 "/db/" 开头的 URL 都需要用户同时拥有 "db" 权限以及 "ROLE_ADMIN" 角色。您会注意到,由于我们使用了 hasRole 表达式,因此不需要指定 "ROLE_" 前缀。
- 任何尚未匹配的 URL 都将被拒绝访问。如果您不想意外地忘记更新授权规则,那么这是一个很好的策略。
Using Path Parameters
此外,Spring Security 提供了一种发现路径参数的机制,因此也可以在 SpEL 表达式中访问这些参数。
例如,您可以通过以下方式访问 SpEL 表达式中的路径参数:
Authorize Request using SpEL path variable
java
<http>
<intercept-url pattern="/resource/{name}" access="#name == authentication.name"/>
<intercept-url pattern="/**" access="authenticated"/>
</http>
这个表达式引用/resource/之后的 path 变量,并要求它等于 Authentication # getName。
Use an Authorization Database, Policy Agent, or Other Service
如果希望将 Spring Security 配置为使用单独的服务进行授权,可以创建自己的 AuthorizationManager 并将其与 anyRequest 匹配。
首先,您的 AuthorizationManager 可能看起来像这样:
Open Policy Agent Authorization Manager
java
@Component
public final class OpenPolicyAgentAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
// make request to Open Policy Agent
}
}
然后,您可以通过以下方式将其连接到 Spring Security:
Any Request Goes to Remote Service
java
@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> authz) throws Exception {
http
// ...
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().access(authz)
);
return http.build();
}
Favor permitAll
over ignoring
当您拥有静态资源时,很容易将筛选器链配置为忽略这些值。一个更安全的方法是允许他们使用 permitAll like so:
Example 4. Permit Static Resources
java
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/css/**").permitAll()
.anyRequest().authenticated()
)
它更加安全,因为即使使用静态资源,编写安全头文件也很重要,如果忽略请求,Spring Security 就无法做到这一点。
在过去,这带来了性能折衷,因为 Spring Security 在每次请求时都会咨询会话。但是,从 SpringSecurity6开始,除非授权规则要求,否则不再 ping 会话。由于现在已经解决了性能影响问题,SpringSecurity 建议对所有请求至少使用 permitAll。
Migrating from authorizeRequests
AuthorizationFilter 为 HttpServletRequest 提供授权。它作为安全筛选器之一插入到 FilterChainProxy 中。
您可以在声明 SecurityFilterChain 时覆盖默认值:
java
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated();
)
// ...
return http.build();
}
这在若干方面改进了 AuthorizeRequest:
- 使用简化的 AuthorizationManager API 而不是元数据源、配置属性、决策管理器和投票者metadata sources, config attributes, decision managers, and voters.。这简化了重用和自定义。
- 延迟 Authentication 查找。而不是为每个请求都查找 Authentication,只有在需要授权决策时才会查找。
- 支持基于 Bean 的配置。
如果使用 AuthorizeHttpRequest 代替 AuthorizeRequest,则使用 AuthorizationFilter 代替 FilterSecurityInterceptor。
Migrating Expressions
在可能的情况下,建议使用类型安全授权管理器而不是 SpEL。对于 Java 配置,WebExpressionAuthorizationManager 可用于帮助迁移遗留的 SpEL。
要使用 WebExpressionAuthorizationManager,您可以使用试图迁移的表达式构造一个表达式,如下所示:
java
.requestMatchers("/test/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
如果您在表达式中引用一个 bean,例如:@webSecurity.check(authentication, request),建议您直接调用 bean,它看起来会像这样:
java
.requestMatchers("/test/**").access((authentication, context) ->
new AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))
对于包含 bean 引用和其他表达式的复杂指令,建议更改这些指令以实现 AuthorizationManager,并通过调用.access(AuthorizationManager)
.
如果不能这样做,可以使用 bean 解析器配置 DefaultHttpSecurityExpressionHandler,并将其提供给 WebExpressionAuthorizationManager # setExpressionHandler。
Security Matchers
RequestMatcher 接口用于确定请求是否与给定的规则匹配。我们使用 securityMatchers 来确定给定的 HttpSecurity 是否应该应用于给定的请求。同样,我们可以使用 requestMatchers 来确定应该应用于给定请求的授权规则。请看以下示例:
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/user/**").hasRole("USER")
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
}
- 将 HttpSecurity 配置为仅应用于以/api/开头的 URL
- 允许具有 USER 角色的用户访问以
/user/
开头的 URL - 允许具有
ADMIN
角色的用户访问以/admin/
开头的 URL - 任何其他与上述规则不匹配的请求都将需要身份验证
SecurityMatcher (s)和 requestMatcher (s)方法将决定哪个 RequestMatcher 实现最适合您的应用程序: 如果 Spring MVC 在类路径中,那么将使用 MvcRequestMatcher,否则将使用 AntPathRequestMatcher。您可以在这里了解更多关于 Spring MVC 集成的信息。
如果您想使用一个特定的 RequestMatcher,只需将一个实现传递给 securityMatcher 和/或 requestMatcher 方法:
java
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) {
// ...
}
}
- 从 AntPathRequestMatcher 和 RegexRequestMatcher 导入静态工厂方法以创建 RequestMatcher 实例。
- 使用 AntPathRequestMatcher 将 HttpSecurity 配置为仅应用于以/api/开头的 URL
- 允许使用 AntPathRequestMatcher 访问以/USER/开头的 URL 到具有 USER 角色的用户
- 允许使用 RegexRequestMatcher 对具有 ADMIN 角色的用户访问以/ADMIN/开头的 URL
- 允许使用自定义 RequestMatcher 访问与具有 SUPERVISOR 角色的用户匹配的 MyCustomRequestMatcher 的 URL
Further Reading
既然已经保护了应用程序的请求,那么就考虑保护它的方法。您还可以进一步阅读关于测试应用程序或将 Spring Security 与应用程序的其他方面(如数据层或跟踪和度量)集成的内容。