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

授权 HttpServletRequests

Spring Security 允许您在请求级别对授权进行建模。 例如,使用 Spring Security,您可以说/admin需要一个权限,而所有其他页面只需要身份验证。spring-doc.cadn.net.cn

默认情况下, Spring Security 要求对每个请求进行身份验证。 也就是说,无论何时使用HttpSecurity实例,则必须声明您的授权规则。spring-doc.cadn.net.cn

只要您有HttpSecurity实例,您至少应该这样做:spring-doc.cadn.net.cn

使用 authorizeHttpRequests
http
    .authorizeHttpRequests((authorize) -> authorize
        .anyRequest().authenticated()
    )
http {
    authorizeHttpRequests {
        authorize(anyRequest, authenticated)
    }
}
<http>
    <intercept-url pattern="/**" access="authenticated"/>
</http>

这告诉 Spring Security,应用程序中的任何端点都要求至少对安全上下文进行身份验证才能允许它。spring-doc.cadn.net.cn

在许多情况下,您的授权规则将比这更复杂,因此请考虑以下使用案例:spring-doc.cadn.net.cn

了解请求授权组件的工作原理

本节以 Servlet 体系结构和实现为基础,深入探讨了基于 Servlet 的应用程序在请求级别如何进行授权
authorization过滤器
图 1.授权 HttpServletRequest

AuthorizationFilter默认是最后一个

AuthorizationFilter默认情况下,在 Spring Security 过滤器链中排在最后。 这意味着 Spring Security 的身份验证过滤器漏洞利用保护和其他过滤器集成不需要授权。 如果您在AuthorizationFilter,他们也不需要授权;否则,他们会。spring-doc.cadn.net.cn

这通常变得很重要的地方是当你添加 Spring MVC 端点时。 因为它们由DispatcherServlet这是在AuthorizationFilter,您的终端节点需要包含在authorizeHttpRequests被允许.spring-doc.cadn.net.cn

所有派单均已获得授权

AuthorizationFilter不仅在每个请求上运行,而且在每个 dispatch 上运行。 这意味着REQUESTdispatch 需要授权,但也需要FORWARDs,ERRORs 和INCLUDEs.spring-doc.cadn.net.cn

例如,Spring MVC 可以FORWARD对呈现 Thymeleaf 模板的视图解析器的请求,如下所示:spring-doc.cadn.net.cn

示例转发 Spring MVC 控制器
@Controller
public class MyController {
    @GetMapping("/endpoint")
    public String endpoint() {
        return "endpoint";
    }
}
@Controller
class MyController {
    @GetMapping("/endpoint")
    fun endpoint(): String {
        return "endpoint"
    }
}

在这种情况下,授权发生两次;一次用于授权/endpoint一次用于转发到 Thymeleaf 以呈现“endpoint”模板。spring-doc.cadn.net.cn

这个原则的另一个例子是 Spring Boot 如何处理错误。 如果容器捕获到异常,请说如下:spring-doc.cadn.net.cn

示例 Error Spring MVC 控制器
@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 会将其调度到ERROR遣。spring-doc.cadn.net.cn

在这种情况下,授权也会发生两次;一次用于授权/endpoint一次用于调度错误。spring-doc.cadn.net.cn

AuthenticationLookup is Deferred

这与authorizeHttpRequests当请求始终被允许或始终被拒绝时。 在这些情况下,Authentication,从而加快请求速度。spring-doc.cadn.net.cn

授权终端节点

您可以通过按优先顺序添加更多规则来将 Spring Security 配置为具有不同的规则。spring-doc.cadn.net.cn

如果您想要求这样做/endpoint只能由具有USER权限,那么您可以执行以下作:spring-doc.cadn.net.cn

授权终端节点
@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 对。spring-doc.cadn.net.cn

AuthorizationFilter按列出的顺序处理这些对,仅将第一个匹配项应用于请求。 这意味着即使 would also match for/**/endpoint以上规则都不是问题。 阅读上述规则的方法是“如果请求是/endpoint,然后需要USER柄;否则,只需要身份验证”。spring-doc.cadn.net.cn

Spring Security 支持多种模式和多种规则;您还可以以编程方式创建自己的 each。spring-doc.cadn.net.cn

获得授权后,您可以通过以下方式使用 Security 的测试支持对其进行测试:spring-doc.cadn.net.cn

测试端点授权
@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());
}

匹配请求

在上面,您已经看到了两种匹配请求的方法spring-doc.cadn.net.cn

您看到的第一个是最简单的,即匹配任何请求。spring-doc.cadn.net.cn

第二种是通过 URI 模式进行匹配。 Spring Security 支持两种语言进行 URI 模式匹配:Ant(如上所示)和正则表达式spring-doc.cadn.net.cn

使用 Ant 进行匹配

Ant 是 Spring Security 用于匹配请求的默认语言。spring-doc.cadn.net.cn

您可以使用它来匹配单个终端节点或目录,甚至可以捕获占位符以供以后使用。 您还可以对其进行优化以匹配一组特定的 HTTP 方法。spring-doc.cadn.net.cn

假设您不想匹配/endpointendpoint 中,您希望匹配/resource目录。 在这种情况下,您可以执行如下作:spring-doc.cadn.net.cn

与 Ant 匹配
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柄;否则,只需要身份验证”spring-doc.cadn.net.cn

您还可以从请求中提取路径值,如下所示:spring-doc.cadn.net.cn

授权和提取
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 的测试支持对其进行测试:spring-doc.cadn.net.cn

测试目录授权
@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 支持针对正则表达式的匹配请求。 如果您想应用比子目录更严格的匹配条件,这会派上用场。**spring-doc.cadn.net.cn

例如,考虑一个包含 username 的路径和所有 username 都必须是字母数字的规则。 您可以使用RegexRequestMatcher要遵守此规则,如下所示:spring-doc.cadn.net.cn

与 Regex 匹配
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 方法匹配规则。 这很方便的一个地方是通过授予的权限进行授权时,例如被授予readwrite特权。spring-doc.cadn.net.cn

要要求所有GETs 的read权限和全部POSTs 的write权限,您可以执行如下作:spring-doc.cadn.net.cn

按 HTTP 方法匹配
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,则 requireread许可;否则,如果请求是 POST,则 requirewrite许可;否则,拒绝请求”spring-doc.cadn.net.cn

默认情况下拒绝请求是一种健康的安全做法,因为它会将规则集变成 allow 名单。

获得授权后,您可以通过以下方式使用 Security 的测试支持对其进行测试:spring-doc.cadn.net.cn

测试 Http 方法授权
@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 授权所有调度程序类型即使REQUESTdispatch 会延续到后续的 dispatch 中,细微的不匹配有时会导致意外的AccessDeniedException.spring-doc.cadn.net.cn

要解决此问题,您可以配置 Spring Security Java 配置以允许调度程序类型,例如FORWARDERROR这样:spring-doc.cadn.net.cn

按 Dispatcher 类型匹配
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)
    }
}

按 Servlet 路径匹配

一般来说,您可以使用requestMatchers(String)如上所示。spring-doc.cadn.net.cn

但是,如果您有来自多个 servlet 的授权规则,则需要指定这些规则:spring-doc.cadn.net.cn

按 PathPatternRequestMatcher 匹配
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.withDefaults;

@Bean
SecurityFilterChain appEndpoints(HttpSecurity http) {
	PathPatternRequestMatcher.Builder mvc = withDefaults().servletPath("/spring-mvc");
	http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers(mvc.matcher("/admin/**")).hasAuthority("admin")
            .requestMatchers(mvc.matcher("/my/controller/**")).hasAuthority("controller")
            .anyRequest().authenticated()
        );

	return http.build();
}
@Bean
fun appEndpoints(http: HttpSecurity): SecurityFilterChain {
    http {
        authorizeHttpRequests {
            authorize("/spring-mvc", "/admin/**", hasAuthority("admin"))
            authorize("/spring-mvc", "/my/controller/**", hasAuthority("controller"))
            authorize(anyRequest, authenticated)
        }
    }
}
<http>
    <intercept-url servlet-path="/spring-mvc" pattern="/admin/**" access="hasAuthority('admin')"/>
    <intercept-url servlet-path="/spring-mvc" pattern="/my/controller/**" access="hasAuthority('controller')"/>
    <intercept-url pattern="/**" access="authenticated"/>
</http>

这是因为 Spring Security 要求所有 URI 都是绝对的(减去上下文路径)。spring-doc.cadn.net.cn

还有其他几个组件可以为您创建请求匹配器,例如PathRequest#toStaticResources#atCommonLocationsspring-doc.cadn.net.cn

使用自定义匹配程序

XML 当前不支持此功能

在 Java 配置中,您可以创建自己的RequestMatcher并将其提供给 DSL,如下所示:spring-doc.cadn.net.cn

按 Dispatcher 类型授权
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 的测试支持对其进行测试:spring-doc.cadn.net.cn

测试自定义授权
@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());
}

授权请求

一旦一个请求被匹配,你就可以用已经看到的几种方式来授权它,比如permitAll,denyAllhasAuthority.spring-doc.cadn.net.cn

作为快速摘要,以下是 DSL 中内置的授权规则:spring-doc.cadn.net.cn

现在,您已经了解了模式、规则以及如何将它们配对在一起,您应该能够理解这个更复杂的示例中发生了什么:spring-doc.cadn.net.cn

授权请求
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 调度FORWARDERROR允许 Spring MVC 渲染视图,允许 Spring Boot 渲染错误
3 我们指定了任何用户都可以访问的多个 URL 模式。 具体来说,如果 URL 以 “/static/” 开头、等于 “/signup” 或等于 “/about”,则任何用户都可以访问请求。
4 任何以 “/admin/” 开头的 URL 都将被限制为具有 “ROLE_ADMIN” 角色的用户。 您会注意到,由于我们正在调用hasRole方法,则不需要指定 “ROLE_” 前缀。
5 任何以 “/db/” 开头的 URL 都要求用户同时被授予 “db” 权限以及 “ROLE_ADMIN”。 您会注意到,由于我们使用的是hasRole表达式,则不需要指定 “ROLE_” 前缀。
6 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。

使用 SPEL 表示授权

使用混凝土时AuthorizationManager,但在某些情况下需要表达式,例如<intercept-url>或使用 JSP Taglibs。 因此,本节将重点介绍这些域中的示例。spring-doc.cadn.net.cn

鉴于此,让我们更深入地介绍 Spring Security 的 Web 安全授权 SPEL API。spring-doc.cadn.net.cn

Spring Security 将其所有授权字段和方法封装在一组根对象中。 最通用的根对象称为SecurityExpressionRoot它构成了WebSecurityExpressionRoot. Spring Security 将此根对象提供给StandardEvaluationContext准备评估授权表达式时。spring-doc.cadn.net.cn

使用授权表达式字段和方法

它提供的第一件事是 SPEL 表达式的一组增强的授权字段和方法。 以下是最常见方法的快速概述:spring-doc.cadn.net.cn

以下是最常见的字段的简要介绍:spring-doc.cadn.net.cn

现在,您已经了解了模式、规则以及如何将它们配对在一起,您应该能够理解这个更复杂的示例中发生了什么:spring-doc.cadn.net.cn

使用 SPEL 授权请求
<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” 角色的用户。 您会注意到,由于我们正在调用hasRole方法,则不需要指定 “ROLE_” 前缀。
3 任何以 “/db/” 开头的 URL 都要求用户同时被授予 “db” 权限以及 “ROLE_ADMIN”。 您会注意到,由于我们使用的是hasRole表达式,则不需要指定 “ROLE_” 前缀。
4 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。

使用路径参数

此外, Spring Security 提供了一种发现路径参数的机制,因此也可以在 SPEL 表达式中访问它们。spring-doc.cadn.net.cn

例如,您可以通过以下方式访问 SPEL 表达式中的 path 参数:spring-doc.cadn.net.cn

使用 SpEL 路径变量授权请求
<http>
    <intercept-url pattern="/resource/{name}" access="#name == authentication.name"/>
    <intercept-url pattern="/**" access="authenticated"/>
</http>

此表达式引用/resource/并要求它等于Authentication#getName.spring-doc.cadn.net.cn

使用授权数据库、策略代理或其他服务

如果要将 Spring Security 配置为使用单独的服务进行授权,则可以创建自己的服务AuthorizationManager并将其匹配到anyRequest.spring-doc.cadn.net.cn

首先,您的AuthorizationManager可能看起来像这样:spring-doc.cadn.net.cn

打开 Policy Agent 授权管理器
@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:spring-doc.cadn.net.cn

任何请求都发送到远程服务
@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> authz) throws Exception {
	http
		// ...
		.authorizeHttpRequests((authorize) -> authorize
            .anyRequest().access(authz)
		);

	return http.build();
}

喜爱permitAllignoring

当您拥有静态资源时,将过滤器链配置为忽略这些值可能很诱人。 更安全的方法是允许他们使用permitAll这样:spring-doc.cadn.net.cn

允许静态资源
http
    .authorizeHttpRequests((authorize) -> authorize
        .requestMatchers("/css/**").permitAll()
        .anyRequest().authenticated()
    )
http {
    authorizeHttpRequests {
        authorize("/css/**", permitAll)
        authorize(anyRequest, authenticated)
    }
}

它更安全,因为即使使用静态资源,编写安全 Headers 也很重要,如果忽略请求,Spring Security 将无法做到这一点。spring-doc.cadn.net.cn

在过去,这需要牺牲性能,因为 Spring Security 在每个请求上都会咨询会话。 但是,从 Spring Security 6 开始,除非授权规则要求,否则不再对会话执行 ping作。 由于现在解决了性能影响,因此 Spring Security 建议至少使用permitAll对于所有请求。spring-doc.cadn.net.cn

迁移自authorizeRequests

AuthorizationFilter取代FilterSecurityInterceptor. 为了保持向后兼容,FilterSecurityInterceptor仍然是默认值。 本节讨论如何AuthorizationFilter有效以及如何覆盖默认配置。

AuthorizationFilter提供HttpServletRequests. 它作为 Security Filters 之一插入到 FilterChainProxy 中。spring-doc.cadn.net.cn

您可以在声明SecurityFilterChain. 不要使用 authorizeRequests,而是使用authorizeHttpRequests这样:spring-doc.cadn.net.cn

使用 authorizeHttpRequests
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated();
        )
        // ...

    return http.build();
}

这改进了authorizeRequests以多种方式:spring-doc.cadn.net.cn

  1. 使用简化的AuthorizationManagerAPI 而不是元数据源、配置属性、决策管理器和投票者。 这简化了重用和定制。spring-doc.cadn.net.cn

  2. 延误Authentication查找。 它不需要为每个请求查找身份验证,而只会在授权决策需要身份验证的请求中查找身份验证。spring-doc.cadn.net.cn

  3. 基于 Bean 的配置支持。spring-doc.cadn.net.cn

什么时候authorizeHttpRequests替换为authorizeRequests然后AuthorizationFilter替换为FilterSecurityInterceptor.spring-doc.cadn.net.cn

迁移表达式

在可能的情况下,建议您使用类型安全的授权管理器而不是 SPEL。 对于 Java 配置,WebExpressionAuthorizationManager可用于帮助迁移旧版 SPEL。spring-doc.cadn.net.cn

要使用WebExpressionAuthorizationManager,您可以使用尝试迁移的表达式构造一个 SET,如下所示:spring-doc.cadn.net.cn

.requestMatchers("/test/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
.requestMatchers("/test/**").access(WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))

如果你在表达式中引用一个 bean,如下所示:@webSecurity.check(authentication, request),建议您改为直接调用 Bean,它看起来类似于以下内容:spring-doc.cadn.net.cn

.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).spring-doc.cadn.net.cn

如果您无法执行此作,则可以配置DefaultHttpSecurityExpressionHandler替换为 bean 解析器,并将其提供给WebExpressionAuthorizationManager#setExpressionhandler.spring-doc.cadn.net.cn

安全匹配器

RequestMatcherinterface 用于确定请求是否与给定规则匹配。 我们使用securityMatchers确定一个给定的HttpSecurity应应用于给定请求。 同样,我们可以使用requestMatchers来确定我们应该应用于给定请求的授权规则。 请看下面的例子:spring-doc.cadn.net.cn

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher("/api/**")                            (1)
			.authorizeHttpRequests(authorize -> authorize
				.requestMatchers("/api/user/**").hasRole("USER")   (2)
				.requestMatchers("/api/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("/api/user/**", hasRole("USER"))                       (2)
                authorize("/api/admin/**", hasRole("ADMIN"))                     (3)
                authorize(anyRequest, authenticated)                             (4)
            }
        }
        return http.build()
    }

}
1 配置HttpSecurity仅应用于以/api/
2 允许访问以/api/user/对于具有USER角色
3 允许访问以/api/admin/对于具有ADMIN角色
4 与上述规则不匹配的任何其他请求都需要身份验证

securityMatcher(s)requestMatcher(s)方法将决定哪些RequestMatcherimplementation 最适合您的应用程序:如果 Spring MVC 在 Classpath 中,则MvcRequestMatcher,否则,AntPathRequestMatcher将被使用。 您可以在此处阅读有关 Spring MVC 集成的更多信息。spring-doc.cadn.net.cn

如果您想使用特定的RequestMatcher,只需将实现传递给securityMatcher和/或requestMatcher方法:spring-doc.cadn.net.cn

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("/api/user/**")).hasRole("USER")     (3)
				.requestMatchers(regexMatcher("/api/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("/api/user/**"), hasRole("USER"))           (3)
                authorize(regexMatcher("/api/admin/**"), hasRole("ADMIN"))       (4)
                authorize(MyCustomRequestMatcher(), hasRole("SUPERVISOR"))       (5)
                authorize(anyRequest, authenticated)
            }
        }
        return http.build()
    }

}
1 导入静态工厂方法AntPathRequestMatcherRegexRequestMatcher创建RequestMatcher实例。
2 配置HttpSecurity仅应用于以/api/AntPathRequestMatcher
3 允许访问以/api/user/对于具有USER角色, 使用AntPathRequestMatcher
4 允许访问以/api/admin/对于具有ADMIN角色, 使用RegexRequestMatcher
5 允许访问与MyCustomRequestMatcher对于具有SUPERVISOR角色, 使用自定义RequestMatcher

延伸阅读

现在,您已经保护了应用程序的请求,请考虑保护其方法。 您还可以进一步阅读有关测试应用程序或将 Spring Security 与应用程序的其他方面(如数据层跟踪和指标)集成的信息。spring-doc.cadn.net.cn