此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.3! |
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.3! |
Spring Security 允许您在请求级别对授权进行建模。
例如,使用 Spring Security,您可以说 下的所有页面都需要一个权限,而所有其他页面只需要身份验证。/admin
默认情况下, Spring Security 要求对每个请求进行身份验证。
也就是说,无论何时使用 HttpSecurity
实例,都必须声明您的授权规则。
无论何时您有一个实例,您至少应该执行以下操作:HttpSecurity
-
Java
-
Kotlin
-
Xml
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
}
<http>
<intercept-url pattern="/**" access="authenticated"/>
</http>
这告诉 Spring Security,应用程序中的任何端点都要求至少对安全上下文进行身份验证才能允许它。
在许多情况下,您的授权规则将比这更复杂,因此请考虑以下使用案例:
-
我有一个使用 authorizeHttpRequests 的应用程序,我想将其迁移到
authorizeHttpRequests
authorizeRequests
-
我想匹配 request,并将 Spring MVC 映射到默认 servlet 以外的其他内容
-
我想授权请求
-
我想将请求授权委托给策略代理
了解请求授权组件的工作原理
本节以 Servlet 体系结构和实现为基础,深入探讨了基于 Servlet 的应用程序在请求级别如何进行授权。 |
-
首先,构造一个从SecurityContextHolder检索Authentication。
AuthorizationFilter
Supplier
-
其次,它将 和 传递给
AuthorizationManager
。 这会将请求与 中的模式进行匹配,并运行相应的规则。Supplier<Authentication>
HttpServletRequest
AuthorizationManager
authorizeHttpRequests
-
如果授权被拒绝,则发布
AuthorizationDeniedEvent
,并引发 an。 在这种情况下,ExceptionTranslationFilter
处理 .AccessDeniedException
AccessDeniedException
-
如果授予访问权限,则会发布
AuthorizationGrantedEvent
并继续使用 FilterChain,从而允许应用程序正常处理。AuthorizationFilter
-
AuthorizationFilter
默认是最后一个
默认情况下,它是 Spring Security 过滤器链中的最后一个。
这意味着 Spring Security 的身份验证过滤器、漏洞利用保护和其他过滤器集成不需要授权。
如果您在 之前添加了自己的过滤器,它们也不需要授权;否则,他们会。AuthorizationFilter
AuthorizationFilter
这通常变得很重要的地方是当你添加 Spring MVC 端点时。
因为它们由 DispatcherServlet
执行,并且这是在 之后执行的,所以您的端点需要包含在 authorizeHttpRequests
中才能获得允许。AuthorizationFilter
所有派单均已获得授权
它不仅在每个请求上运行,而且在每个调度上运行。
这意味着 dispatch 需要 authorization,但也需要 s、s 和 s。AuthorizationFilter
REQUEST
FORWARD
ERROR
INCLUDE
例如, Spring MVC 可以向呈现 Thymeleaf 模板的视图解析器发出请求,如下所示:FORWARD
-
Java
-
Kotlin
@Controller
public class MyController {
@GetMapping("/endpoint")
public String endpoint() {
return "endpoint";
}
}
@Controller
class MyController {
@GetMapping("/endpoint")
fun endpoint(): String {
return "endpoint"
}
}
在这种情况下,授权发生两次;一次用于授权,一次用于转发到 Thymeleaf 以呈现“endpoint”模板。/endpoint
因此,您可能希望允许所有 FORWARD
分派。
这个原则的另一个例子是 Spring Boot 如何处理错误。 如果容器捕获到异常,请说如下:
-
Java
-
Kotlin
@Controller
public class MyController {
@GetMapping("/endpoint")
public String endpoint() {
throw new UnsupportedOperationException("unsupported");
}
}
@Controller
class MyController {
@GetMapping("/endpoint")
fun endpoint(): String {
throw UnsupportedOperationException("unsupported")
}
}
然后 Boot 会将其 dispatch 给 dispatch。ERROR
在这种情况下,授权也会发生两次;一次用于授权,一次用于调度错误。/endpoint
因此,您可能希望允许所有 ERROR
调度。
Authentication
Lookup is Deferred
这与何时始终允许或始终拒绝请求有关。
在这些情况下,不会查询 Authentication
,从而加快请求速度。authorizeHttpRequests
本节以 Servlet 体系结构和实现为基础,深入探讨了基于 Servlet 的应用程序在请求级别如何进行授权。 |
授权终端节点
您可以通过按优先顺序添加更多规则来将 Spring Security 配置为具有不同的规则。
如果要要求只有具有权限的最终用户才能访问,则可以执行以下操作:/endpoint
USER
-
Java
-
Kotlin
-
Xml
@Bean
public SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/endpoint").hasAuthority("USER")
.anyRequest().authenticated()
)
// ...
return http.build();
}
@Bean
fun web(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/endpoint", hasAuthority("USER"))
authorize(anyRequest, authenticated)
}
}
return http.build()
}
<http>
<intercept-url pattern="/endpoint" access="hasAuthority('USER')"/>
<intercept-url pattern="/**" access="authenticated"/>
</http>
如您所见,声明可以分解为 pattern/rule 对。
AuthorizationFilter
按列出的顺序处理这些对,仅将第一个匹配项应用于请求。
这意味着即使 will also match for above rules 也不是问题。
解读上述规则的方法是“如果请求是 ,则需要权限;否则,只需要身份验证”。/**
/endpoint
/endpoint
USER
Spring Security 支持多种模式和多种规则;您还可以以编程方式创建自己的 each。
获得授权后,您可以通过以下方式使用 Security 的测试支持对其进行测试:
-
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());
}
匹配请求
在上面,您已经看到了两种匹配请求的方法。
您看到的第一个是最简单的,即匹配任何请求。
使用 Ant 进行匹配
Ant 是 Spring Security 用于匹配请求的默认语言。
您可以使用它来匹配单个终端节点或目录,甚至可以捕获占位符以供以后使用。 您还可以对其进行优化以匹配一组特定的 HTTP 方法。
假设您不想匹配终端节点,而是希望匹配目录下的所有终端节点。
在这种情况下,您可以执行如下操作:/endpoint
/resource
-
Java
-
Kotlin
-
Xml
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/resource/**").hasAuthority("USER")
.anyRequest().authenticated()
)
http {
authorizeHttpRequests {
authorize("/resource/**", hasAuthority("USER"))
authorize(anyRequest, authenticated)
}
}
<http>
<intercept-url pattern="/resource/**" access="hasAuthority('USER')"/>
<intercept-url pattern="/**" access="authenticated"/>
</http>
解读方式是 “如果请求是或某个子目录,则需要权限;否则,只需要身份验证”/resource
USER
您还可以从请求中提取路径值,如下所示:
-
Java
-
Kotlin
-
Xml
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/resource/{name}").access(new WebExpressionAuthorizationManager("#name == authentication.name"))
.anyRequest().authenticated()
)
http {
authorizeHttpRequests {
authorize("/resource/{name}", WebExpressionAuthorizationManager("#name == authentication.name"))
authorize(anyRequest, authenticated)
}
}
<http>
<intercept-url pattern="/resource/{name}" access="#name == authentication.name"/>
<intercept-url pattern="/**" access="authenticated"/>
</http>
获得授权后,您可以通过以下方式使用 Security 的测试支持对其进行测试:
-
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());
}
Spring Security 仅匹配 paths。 如果要匹配查询参数,则需要自定义请求匹配器。 |
使用正则表达式进行匹配
Spring Security 支持针对正则表达式的匹配请求。
如果您想应用比子目录更严格的匹配条件,这会派上用场。**
例如,考虑一个包含 username 的路径和所有 username 都必须是字母数字的规则。
您可以使用 RegexRequestMatcher
来遵守此规则,如下所示:
-
Java
-
Kotlin
-
Xml
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(RegexRequestMatcher.regexMatcher("/resource/[A-Za-z0-9]+")).hasAuthority("USER")
.anyRequest().denyAll()
)
http {
authorizeHttpRequests {
authorize(RegexRequestMatcher.regexMatcher("/resource/[A-Za-z0-9]+"), hasAuthority("USER"))
authorize(anyRequest, denyAll)
}
}
<http>
<intercept-url request-matcher="regex" pattern="/resource/[A-Za-z0-9]+" access="hasAuthority('USER')"/>
<intercept-url pattern="/**" access="denyAll"/>
</http>
通过 Http 方法匹配
您还可以通过 HTTP 方法匹配规则。
这很方便的一个地方是通过授予的权限进行授权时,例如被授予 or 特权。read
write
要要求所有 s 都具有权限,所有 s 都具有权限,您可以执行如下操作:GET
read
POST
write
-
Java
-
Kotlin
-
Xml
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(HttpMethod.GET).hasAuthority("read")
.requestMatchers(HttpMethod.POST).hasAuthority("write")
.anyRequest().denyAll()
)
http {
authorizeHttpRequests {
authorize(HttpMethod.GET, hasAuthority("read"))
authorize(HttpMethod.POST, hasAuthority("write"))
authorize(anyRequest, denyAll)
}
}
<http>
<intercept-url http-method="GET" pattern="/**" access="hasAuthority('read')"/>
<intercept-url http-method="POST" pattern="/**" access="hasAuthority('write')"/>
<intercept-url pattern="/**" access="denyAll"/>
</http>
这些授权规则应如下所示:“如果请求是 GET,则需要权限;否则,如果请求是 POST,则需要权限;否则,拒绝请求”read
write
默认情况下拒绝请求是一种健康的安全做法,因为它会将规则集变成 allow 名单。 |
获得授权后,您可以通过以下方式使用 Security 的测试支持对其进行测试:
-
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());
}
按 Dispatcher 类型匹配
XML 当前不支持此功能 |
如前所述,默认情况下, Spring Security 授权所有调度程序类型。
即使 dispatch 上建立的安全上下文会延续到后续 dispatch 中,但细微的不匹配有时会导致意外的 .REQUEST
AccessDeniedException
要解决此问题,您可以配置 Spring Security Java 配置以允许调度程序类型,例如 和 ,如下所示:FORWARD
ERROR
-
Java
-
Kotlin
http
.authorizeHttpRequests((authorize) -> authorize
.dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
.requestMatchers("/endpoint").permitAll()
.anyRequest().denyAll()
)
http {
authorizeHttpRequests {
authorize(DispatcherTypeRequestMatcher(DispatcherType.FORWARD), permitAll)
authorize(DispatcherTypeRequestMatcher(DispatcherType.ERROR), permitAll)
authorize("/endpoint", permitAll)
authorize(anyRequest, denyAll)
}
}
使用 MvcRequestMatcher
一般来说,您可以如上所述使用。requestMatchers(String)
但是,如果将 Spring MVC 映射到不同的 servlet 路径,则需要在安全配置中考虑这一点。
例如,如果 Spring MVC 被映射到而不是(默认),那么你可能有一个你想要授权的端点。/spring-mvc
/
/spring-mvc/my/controller
您需要在配置中拆分 servlet 路径和控制器路径,如下所示:MvcRequestMatcher
-
Java
-
Kotlin
-
Xml
@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();
}
@Bean
fun mvc(introspector: HandlerMappingIntrospector): MvcRequestMatcher.Builder =
MvcRequestMatcher.Builder(introspector).servletPath("/spring-mvc");
@Bean
fun appEndpoints(http: HttpSecurity, mvc: MvcRequestMatcher.Builder): SecurityFilterChain =
http {
authorizeHttpRequests {
authorize(mvc.pattern("/my/controller/**"), hasAuthority("controller"))
authorize(anyRequest, authenticated)
}
}
<http>
<intercept-url servlet-path="/spring-mvc" pattern="/my/controller/**" access="hasAuthority('controller')"/>
<intercept-url pattern="/**" access="authenticated"/>
</http>
这种需求至少可以通过两种不同的方式出现:
-
如果使用 Boot 属性将默认路径 () 更改为其他路径
spring.mvc.servlet.path
/
-
如果你注册了多个 Spring MVC(因此要求其中一个不是默认路径)
DispatcherServlet
使用自定义匹配程序
XML 当前不支持此功能 |
在 Java 配置中,你可以创建自己的 RequestMatcher
并将其提供给 DSL,如下所示:
-
Java
-
Kotlin
RequestMatcher printview = (request) -> request.getParameter("print") != null;
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(printview).hasAuthority("print")
.anyRequest().authenticated()
)
val printview: RequestMatcher = { (request) -> request.getParameter("print") != null }
http {
authorizeHttpRequests {
authorize(printview, hasAuthority("print"))
authorize(anyRequest, authenticated)
}
}
由于 RequestMatcher 是一个功能接口,因此您可以在 DSL 中将其作为 lambda 提供。
但是,如果要从请求中提取值,则需要有一个具体的类,因为这需要重写方法。default |
获得授权后,您可以通过以下方式使用 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());
}
Spring Security 仅匹配 paths。 如果要匹配查询参数,则需要自定义请求匹配器。 |
默认情况下拒绝请求是一种健康的安全做法,因为它会将规则集变成 allow 名单。 |
XML 当前不支持此功能 |
XML 当前不支持此功能 |
由于 RequestMatcher 是一个功能接口,因此您可以在 DSL 中将其作为 lambda 提供。
但是,如果要从请求中提取值,则需要有一个具体的类,因为这需要重写方法。default |
授权请求
匹配请求后,您可以通过多种方式对其进行授权,例如 、 和 。permitAll
denyAll
hasAuthority
作为快速摘要,以下是 DSL 中内置的授权规则:
-
permitAll
- 该请求不需要授权,并且是公共端点;请注意,在这种情况下,永远不会从会话中检索Authentication
-
denyAll
- 在任何情况下都不允许该请求;请注意,在这种情况下,永远不会从会话中检索Authentication
-
hasAuthority
- 该请求要求 具有与给定值匹配的GrantedAuthority
Authentication
-
hasRole
- 该前缀的快捷方式或配置为默认前缀的任何内容hasAuthority
ROLE_
-
hasAnyAuthority
- 请求要求 have a 与任何给定值匹配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();
}
1 | 指定了多个授权规则。 每个规则都按照其声明的顺序进行考虑。 |
2 | 调度并允许 Spring MVC 渲染视图,允许 Spring Boot 渲染错误FORWARD ERROR |
3 | 我们指定了任何用户都可以访问的多个 URL 模式。 具体来说,如果 URL 以 “/static/” 开头、等于 “/signup” 或等于 “/about”,则任何用户都可以访问请求。 |
4 | 任何以 “/admin/” 开头的 URL 都将被限制为具有 “ROLE_ADMIN” 角色的用户。
您会注意到,由于我们正在调用该方法,因此不需要指定 “ROLE_” 前缀。hasRole |
5 | 任何以 “/db/” 开头的 URL 都要求用户同时被授予 “db” 权限以及 “ROLE_ADMIN”。
您会注意到,由于我们使用的是表达式,因此不需要指定 “ROLE_” 前缀。hasRole |
6 | 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。 |
1 | 指定了多个授权规则。 每个规则都按照其声明的顺序进行考虑。 |
2 | 调度并允许 Spring MVC 渲染视图,允许 Spring Boot 渲染错误FORWARD ERROR |
3 | 我们指定了任何用户都可以访问的多个 URL 模式。 具体来说,如果 URL 以 “/static/” 开头、等于 “/signup” 或等于 “/about”,则任何用户都可以访问请求。 |
4 | 任何以 “/admin/” 开头的 URL 都将被限制为具有 “ROLE_ADMIN” 角色的用户。
您会注意到,由于我们正在调用该方法,因此不需要指定 “ROLE_” 前缀。hasRole |
5 | 任何以 “/db/” 开头的 URL 都要求用户同时被授予 “db” 权限以及 “ROLE_ADMIN”。
您会注意到,由于我们使用的是表达式,因此不需要指定 “ROLE_” 前缀。hasRole |
6 | 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。 |
使用 SPEL 表示授权
虽然建议使用具体表达式,但在某些情况下需要表达式,例如使用 JSP Taglibs 或使用 JSP Taglibs。
因此,本节将重点介绍这些域中的示例。AuthorizationManager
<intercept-url>
鉴于此,让我们更深入地介绍 Spring Security 的 Web 安全授权 SPEL API。
Spring Security 将其所有授权字段和方法封装在一组根对象中。
最通用的根对象称为 ,它构成了 的基础。
Spring Security 在准备评估授权表达式时提供此根对象。SecurityExpressionRoot
WebSecurityExpressionRoot
StandardEvaluationContext
使用授权表达式字段和方法
它提供的第一件事是 SPEL 表达式的一组增强的授权字段和方法。 以下是最常见方法的快速概述:
-
permitAll
- 请求不需要授权即可调用;请注意,在这种情况下,永远不会从会话中检索Authentication
-
denyAll
- 在任何情况下都不允许该请求;请注意,在这种情况下,永远不会从会话中检索Authentication
-
hasAuthority
- 该请求要求 具有与给定值匹配的GrantedAuthority
Authentication
-
hasRole
- 该前缀的快捷方式或配置为默认前缀的任何内容hasAuthority
ROLE_
-
hasAnyAuthority
- 请求要求 have a 与任何给定值匹配Authentication
GrantedAuthority
-
hasAnyRole
- 该前缀的快捷方式或配置为默认前缀的任何内容hasAnyAuthority
ROLE_
-
hasPermission
- 实例中的钩子,用于执行对象级授权PermissionEvaluator
以下是最常见的字段的简要介绍:
-
authentication
- 与此方法调用关联的实例Authentication
-
principal
- 与此方法调用关联的Authentication#getPrincipal
现在,您已经了解了模式、规则以及如何将它们配对在一起,您应该能够理解这个更复杂的示例中发生了什么:
-
Xml
<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>
1 | 我们指定了任何用户都可以访问的 URL 模式。 具体来说,如果 URL 以 “/static/” 开头,则任何用户都可以访问请求。 |
2 | 任何以 “/admin/” 开头的 URL 都将被限制为具有 “ROLE_ADMIN” 角色的用户。
您会注意到,由于我们正在调用该方法,因此不需要指定 “ROLE_” 前缀。hasRole |
3 | 任何以 “/db/” 开头的 URL 都要求用户同时被授予 “db” 权限以及 “ROLE_ADMIN”。
您会注意到,由于我们使用的是表达式,因此不需要指定 “ROLE_” 前缀。hasRole |
4 | 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。 |
使用路径参数
此外, Spring Security 提供了一种发现路径参数的机制,因此也可以在 SPEL 表达式中访问它们。
例如,您可以通过以下方式访问 SPEL 表达式中的 path 参数:
-
Xml
<http>
<intercept-url pattern="/resource/{name}" access="#name == authentication.name"/>
<intercept-url pattern="/**" access="authenticated"/>
</http>
此表达式引用 path 变量 after,并要求它等于 。/resource/
Authentication#getName
使用授权数据库、策略代理或其他服务
如果要将 Spring Security 配置为使用单独的服务进行授权,则可以创建自己的服务并将其与 匹配。AuthorizationManager
anyRequest
首先,你的可能看起来像这样:AuthorizationManager
-
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:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> authz) throws Exception {
http
// ...
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().access(authz)
);
return http.build();
}
偏爱permitAll
ignoring
当您拥有静态资源时,将过滤器链配置为忽略这些值可能很诱人。
更安全的方法是允许他们使用如下:permitAll
-
Java
-
Kotlin
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/css/**").permitAll()
.anyRequest().authenticated()
)
http {
authorizeHttpRequests {
authorize("/css/**", permitAll)
authorize(anyRequest, authenticated)
}
}
它更安全,因为即使使用静态资源,编写安全 Headers 也很重要,如果忽略请求,Spring Security 将无法做到这一点。
在过去,这需要牺牲性能,因为 Spring Security 在每个请求上都会咨询会话。
但是,从 Spring Security 6 开始,除非授权规则要求,否则不再对会话执行 ping 操作。
由于现在解决了性能影响,因此 Spring Security 建议至少对所有请求使用 THE SECURE。permitAll
1 | 我们指定了任何用户都可以访问的 URL 模式。 具体来说,如果 URL 以 “/static/” 开头,则任何用户都可以访问请求。 |
2 | 任何以 “/admin/” 开头的 URL 都将被限制为具有 “ROLE_ADMIN” 角色的用户。
您会注意到,由于我们正在调用该方法,因此不需要指定 “ROLE_” 前缀。hasRole |
3 | 任何以 “/db/” 开头的 URL 都要求用户同时被授予 “db” 权限以及 “ROLE_ADMIN”。
您会注意到,由于我们使用的是表达式,因此不需要指定 “ROLE_” 前缀。hasRole |
4 | 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。 |
迁移自authorizeRequests
AuthorizationFilter 取代 FilterSecurityInterceptor 。
为了保持向后兼容,请保留默认值。
本节讨论工作原理以及如何覆盖默认配置。FilterSecurityInterceptor AuthorizationFilter |
AuthorizationFilter
为 s.
它作为 Security Filters 之一插入到 FilterChainProxy 中。HttpServletRequest
您可以在声明 .
不要使用 authorizeRequests,而是使用 ,如下所示:SecurityFilterChain
authorizeHttpRequests
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated();
)
// ...
return http.build();
}
这在许多方面都有所改进:authorizeRequests
-
使用简化的 API,而不是元数据源、配置属性、决策管理器和投票者。 这简化了重用和定制。
AuthorizationManager
-
延迟查找。 它不需要为每个请求查找身份验证,而只会在授权决策需要身份验证的请求中查找身份验证。
Authentication
-
基于 Bean 的配置支持。
When 使用 而不是 ,则使用 AuthorizationFilter
而不是 FilterSecurityInterceptor
。authorizeHttpRequests
authorizeRequests
迁移表达式
在可能的情况下,建议您使用类型安全的授权管理器而不是 SPEL。
对于 Java 配置,WebExpressionAuthorizationManager
可用于帮助迁移旧版 SPEL。
要使用 ,您可以使用尝试迁移的表达式构造一个 TOP,如下所示:WebExpressionAuthorizationManager
-
Java
-
Kotlin
.requestMatchers("/test/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
.requestMatchers("/test/**").access(WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
如果您在表达式中引用 bean,如下所示:,建议您直接调用该 bean,如下所示:@webSecurity.check(authentication, request)
-
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 引用以及其他表达式的复杂指令,建议您通过调用来更改这些指令以 implement 并引用它们。AuthorizationManager
.access(AuthorizationManager)
如果您无法做到这一点,则可以使用 bean 解析器配置 DefaultHttpSecurityExpressionHandler
并将其提供给。WebExpressionAuthorizationManager#setExpressionhandler
AuthorizationFilter 取代 FilterSecurityInterceptor 。
为了保持向后兼容,请保留默认值。
本节讨论工作原理以及如何覆盖默认配置。FilterSecurityInterceptor AuthorizationFilter |
安全匹配器
RequestMatcher
接口用于确定请求是否与给定规则匹配。
我们用于确定是否应将给定的 HttpSecurity
应用于给定的请求。
同样,我们可以使用来确定我们应该应用于给定请求的授权规则。
请看下面的例子:securityMatchers
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 | 与上述规则不匹配的任何其他请求都需要身份验证 |
and 方法将决定哪种实现最适合您的应用程序:如果 Spring MVC 在 Classpath 中,则将使用 MvcRequestMatcher
,否则将使用 AntPathRequestMatcher
。
您可以在此处阅读有关 Spring MVC 集成的更多信息。securityMatcher(s)
requestMatcher(s)
RequestMatcher
如果要使用特定的 ,只需将实现传递给 and/or 方法: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 | 配置为仅应用于以 开头的 URL,使用HttpSecurity /api/ AntPathRequestMatcher |
3 | 允许具有该角色的用户访问以 开头的 URL/user/ USER AntPathRequestMatcher |
4 | 允许具有该角色的用户访问以 开头的 URL/admin/ ADMIN RegexRequestMatcher |
5 | 允许访问与具有该角色的用户匹配的 URL,使用自定义MyCustomRequestMatcher SUPERVISOR RequestMatcher |
1 | 配置为仅应用于以HttpSecurity /api/ |
2 | 允许具有/user/ USER |
3 | 允许具有/admin/ ADMIN |
4 | 与上述规则不匹配的任何其他请求都需要身份验证 |
1 | 从 和 导入静态工厂方法以创建实例。AntPathRequestMatcher RegexRequestMatcher RequestMatcher |
2 | 配置为仅应用于以 开头的 URL,使用HttpSecurity /api/ AntPathRequestMatcher |
3 | 允许具有该角色的用户访问以 开头的 URL/user/ USER AntPathRequestMatcher |
4 | 允许具有该角色的用户访问以 开头的 URL/admin/ ADMIN RegexRequestMatcher |
5 | 允许访问与具有该角色的用户匹配的 URL,使用自定义MyCustomRequestMatcher SUPERVISOR RequestMatcher |