对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

以下步骤与有关如何执行授权的更改有关。Spring中文文档

用于方法安全性AuthorizationManager

如果您在进行这些更改时遇到麻烦,请注意,虽然已弃用,但在 6.0 中不会被删除,允许您通过坚持使用旧注释来选择退出。@EnableGlobalMethodSecuritySpring中文文档

全局方法安全性替换为方法安全性

@EnableGlobalMethodSecurity<global-method-security> 分别被弃用,取而代之的是 @EnableMethodSecurity<method-security>。 默认情况下,新的注解和 XML 元素会激活 Spring 的前后注解并在内部使用。AuthorizationManagerSpring中文文档

这意味着以下两个列表在功能上是等效的:Spring中文文档

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
<global-method-security pre-post-enabled="true"/>
@EnableMethodSecurity
@EnableMethodSecurity
<method-security/>

对于不使用前后批注的应用程序,请确保将其关闭以避免激活不需要的行为。Spring中文文档

例如,像这样的列表:Spring中文文档

@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableGlobalMethodSecurity(securedEnabled = true)
<global-method-security secured-enabled="true"/>

应更改为:Spring中文文档

@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
<method-security secured-enabled="true" pre-post-enabled="false"/>

更改order@EnableTransactionManagement

@EnableTransactionManagement并具有相同的值 。 这意味着它们在Spring AOP Advisor链中相对于彼此的顺序是不确定的。@EnableGlobalMethodSecurityorderInteger.MAX_VALUESpring中文文档

这通常没问题,因为大多数方法安全表达式不需要打开的事务即可正常运行;然而,从历史上看,有时有必要通过设定其值来确保一个发生在另一个之前。orderSpring中文文档

@EnableMethodSecurity没有值,因为它会发布多个侦听器。 事实上,它不能尝试向后兼容,因为它不能将所有拦截器都设置在同一个顾问链位置。order@EnableTransactionManagementSpring中文文档

相反,侦听器的值基于偏移量 0。 拦截器的顺序为100;, 200;等等。@EnableMethodSecurity@PreFilter@PostAuthorizeSpring中文文档

因此,如果在更新后发现方法安全表达式由于没有打开的事务而不起作用,请从以下内容更改事务注释定义:Spring中文文档

@EnableTransactionManagement
@EnableTransactionManagement
<tx:annotation-driven ref="txManager"/>
@EnableTransactionManagement(order = 0)
@EnableTransactionManagement(order = 0)
<tx:annotation-driven ref="txManager" order="0"/>

这样,交易 AOP 建议将放在 Spring Security 的建议之前,并且当您的授权 SpEL 表达式被评估时,交易将打开。Spring中文文档

使用自定义而不是子类化@BeanDefaultMethodSecurityExpressionHandler

作为性能优化,引入了一种新方法,该方法采用 a 而不是 .MethodSecurityExpressionHandlerSupplier<Authentication>AuthenticationSpring中文文档

这允许 Spring Security 延迟查找 ,并在使用而不是 时自动利用。Authentication@EnableMethodSecurity@EnableGlobalMethodSecuritySpring中文文档

但是,假设您的代码扩展并重写以返回自定义实例。 这将不再起作用,因为设置呼叫的安排。DefaultMethodSecurityExpressionHandlercreateSecurityExpressionRoot(Authentication, MethodInvocation)SecurityExpressionRoot@EnableMethodSecuritycreateEvaluationContext(Supplier<Authentication>, MethodInvocation)Spring中文文档

令人高兴的是,这种级别的定制通常是不必要的。 相反,您可以使用所需的授权方法创建自定义 Bean。Spring中文文档

例如,假设您想要 的自定义计算。 您可以创建如下自定义项:@PostAuthorize("hasAuthority('ADMIN')")@BeanSpring中文文档

class MyAuthorizer {
	boolean isAdmin(MethodSecurityExpressionOperations root) {
		boolean decision = root.hasAuthority("ADMIN");
		// custom work ...
        return decision;
	}
}
class MyAuthorizer {
	fun isAdmin(val root: MethodSecurityExpressionOperations): boolean {
		val decision = root.hasAuthority("ADMIN");
		// custom work ...
        return decision;
	}
}

然后在注释中引用它,如下所示:Spring中文文档

@PreAuthorize("@authz.isAdmin(#root)")
@PreAuthorize("@authz.isAdmin(#root)")

我仍然更喜欢子类DefaultMethodSecurityExpressionHandler

如果必须继续子类化,您仍然可以这样做。 相反,重写该方法,如下所示:DefaultMethodSecurityExpressionHandlercreateEvaluationContext(Supplier<Authentication>, MethodInvocation)Spring中文文档

@Component
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
    @Override
    public EvaluationContext createEvaluationContext(
            Supplier<Authentication> authentication, MethodInvocation mi) {
		StandardEvaluationContext context = (StandardEvaluationContext) super.createEvaluationContext(authentication, mi);
        MySecurityExpressionRoot root = new MySecurityExpressionRoot(authentication, invocation);
	    root.setPermissionEvaluator(getPermissionEvaluator());
	    root.setTrustResolver(new AuthenticationTrustResolverImpl());
        root.setRoleHierarchy(getRoleHierarchy());
        context.setRootObject(root);
        return context;
    }
}
@Component
class MyExpressionHandler: DefaultMethodSecurityExpressionHandler {
    override fun createEvaluationContext(val authentication: Supplier<Authentication>,
        val mi: MethodInvocation): EvaluationContext {
		val context = super.createEvaluationContext(authentication, mi) as StandardEvaluationContext;
        val root = new MySecurityExpressionRoot(authentication, invocation);
	    root.setPermissionEvaluator(getPermissionEvaluator());
	    root.setTrustResolver(new AuthenticationTrustResolverImpl());
        root.setRoleHierarchy(getRoleHierarchy());
        context.setRootObject(root);
        return context;
    }
}

选择退出步骤

如果您需要选择退出这些更改,则可以使用代替@EnableGlobalMethodSecurity@EnableMethodSecuritySpring中文文档

发布 a 而不是MethodSecurityExpressionHandlerPermissionEvaluator

@EnableMethodSecurity不拾取 . 这有助于保持其 API 简单。PermissionEvaluatorSpring中文文档

如果您有自定义 PermissionEvaluator ,请将其从以下位置更改为:@BeanSpring中文文档

@Bean
static PermissionEvaluator permissionEvaluator() {
	// ... your evaluator
}
companion object {
	@Bean
	fun permissionEvaluator(): PermissionEvaluator {
		// ... your evaluator
	}
}
@Bean
static MethodSecurityExpressionHandler expressionHandler() {
	var expressionHandler = new DefaultMethodSecurityExpressionHandler();
	expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
	return expressionHandler;
}
companion object {
	@Bean
	fun expressionHandler(): MethodSecurityExpressionHandler {
		val expressionHandler = DefaultMethodSecurityExpressionHandler
		expressionHandler.setPermissionEvaluator(myPermissionEvaluator)
		return expressionHandler
	}
}

替换任何自定义方法-security sAccessDecisionManager

应用程序可能具有自定义的 AccessDecisionManagerAccessDecisionVoter 排列。 准备策略将取决于您进行每次安排的原因。 请继续阅读以找到最适合您情况的匹配。Spring中文文档

我使用UnanimousBased

如果应用程序将 UnanimousBased 与默认投票者一起使用,则可能不需要执行任何操作,因为基于一致是 @EnableMethodSecurity 的默认行为。Spring中文文档

但是,如果您发现无法接受默认授权管理器,则可以使用它来编写自己的安排。AuthorizationManagers.allOfSpring中文文档

请注意,与 有一个区别,即如果所有代表都弃权,则授予授权。 如果必须在所有委托都弃权时拒绝授权,请实现一个复合 AuthorizationManager,该组合将委托 s 集考虑在内。allOfAuthorizationManagerSpring中文文档

完成此操作后,请按照参考手册中的详细信息添加自定义 AuthorizationManagerSpring中文文档

我使用AffirmativeBased

如果应用程序使用 AffirmativeBased,则可以构造等效的 AuthorizationManager,如下所示:Spring中文文档

AuthorizationManager<MethodInvocation> authorization = AuthorizationManagers.anyOf(
		// ... your list of authorization managers
)
val authorization = AuthorizationManagers.anyOf(
		// ... your list of authorization managers
)

实现后,请按照参考手册中的详细信息添加自定义 AuthorizationManagerAuthorizationManagerSpring中文文档

我使用ConsensusBased

没有框架提供的等效项。 在这种情况下,请实现一个复合 AuthorizationManager,该组合将委托 s 集考虑在内。AuthorizationManagerSpring中文文档

实现后,请按照参考手册中的详细信息添加自定义 AuthorizationManagerAuthorizationManagerSpring中文文档

我使用自定义AccessDecisionVoter

应更改类以实现 AuthorizationManager 或创建适配器。Spring中文文档

如果不知道您的自定义投票者在做什么,就不可能推荐通用解决方案。 举个例子,下面是调整 SecurityMetadataSourceAccessDecisionVoter 的样子:@PreAuthorizeSpring中文文档

public final class PreAuthorizeAuthorizationManagerAdapter implements AuthorizationManager<MethodInvocation> {
    private final SecurityMetadataSource metadata;
    private final AccessDecisionVoter voter;

    public PreAuthorizeAuthorizationManagerAdapter(MethodSecurityExpressionHandler expressionHandler) {
        ExpressionBasedAnnotationAttributeFactory attributeFactory =
                new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
        this.metadata = new PrePostAnnotationSecurityMetadataSource(attributeFactory);
        ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
        expressionAdvice.setExpressionHandler(expressionHandler);
        this.voter = new PreInvocationAuthorizationAdviceVoter(expressionAdvice);
    }

    public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
        List<ConfigAttribute> attributes = this.metadata.getAttributes(invocation, AopUtils.getTargetClass(invocation.getThis()));
        int decision = this.voter.vote(authentication.get(), invocation, attributes);
        if (decision == ACCESS_GRANTED) {
            return new AuthorizationDecision(true);
        }
        if (decision == ACCESS_DENIED) {
            return new AuthorizationDecision(false);
        }
        return null; // abstain
    }
}

实现后,请按照参考手册中的详细信息添加自定义 AuthorizationManagerAuthorizationManagerSpring中文文档

我使用或AfterInvocationManagerAfterInvocationProvider

AfterInvocationManagerAfterInvocationProvider 对调用结果做出授权决定。 例如,在方法调用的情况下,它们对方法的返回值做出授权决策。Spring中文文档

在 Spring Security 3.0 中,授权决策被标准化为 @PostAuthorize@PostFilter解。 用于决定是否允许返回整个返回值。 用于从返回的集合、数组或流中筛选单个条目。@PostAuthorize@PostFilterSpring中文文档

这两个注释应该可以满足大多数需求,我们鼓励您迁移到其中一个或两个,因为现在已经弃用了。AfterInvocationProviderAfterInvocationManagerSpring中文文档

如果你已经实现了自己的 or ,你应该首先问问自己它试图做什么。 如果它尝试授权返回类型,请考虑实现 AuthorizationManager<MethodInvocationResult>并使用 AfterMethodAuthorizationManagerInterceptor。或者发布自定义 Bean 并使用 .AfterInvocationManagerAfterInvocationProvider@PostAuthorize("@myBean.authorize(#root)")Spring中文文档

如果它正在尝试过滤,则考虑发布自定义 Bean 并使用 . 或者,如果需要,您可以实现自己的 ,看看 和 举个例子。@PostFilter("@mybean.authorize(#root)")MethodInterceptorPostFilterAuthorizationMethodInterceptorPrePostMethodSecurityConfigurationSpring中文文档

我使用RunAsManager

目前没有 RunAsManager 的替代品,但正在考虑一个替代品。Spring中文文档

但是,如果需要,将 调整为 API 非常简单。RunAsManagerAuthorizationManagerSpring中文文档

下面是一些帮助您入门的伪代码:Spring中文文档

public final class RunAsAuthorizationManagerAdapter<T> implements AuthorizationManager<T> {
	private final RunAsManager runAs = new RunAsManagerImpl();
	private final SecurityMetadataSource metadata;
    private final AuthorizationManager<T> authorization;

    // ... constructor

    public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
		Supplier<Authentication> wrapped = (auth) -> {
			List<ConfigAttribute> attributes = this.metadata.getAttributes(object);
			return this.runAs.buildRunAs(auth, object, attributes);
		};
		return this.authorization.check(wrapped, object);
    }
}

实现后,请按照参考手册中的详细信息添加自定义 AuthorizationManagerAuthorizationManagerSpring中文文档

检查 sAnnotationConfigurationException

@EnableMethodSecurity并更严格地执行 Spring Security 的不可重复或不兼容的注释。 如果在移动到任一选项后在日志中看到 s,请按照异常消息中的说明清理应用程序的方法安全批注用法。<method-security>AnnotationConfigurationExceptionSpring中文文档

用于消息安全AuthorizationManager

如果您在进行这些更改时遇到问题,可以按照本节末尾的选择退出步骤进行操作。Spring中文文档

确保所有邮件都定义了授权规则

默认情况下,现已弃用的消息安全支持允许所有消息。新支持具有更强的默认值,即拒绝所有消息。Spring中文文档

为此,请确保为每个请求声明了授权规则。Spring中文文档

例如,应用程序配置如下:Spring中文文档

@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
    messages
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN");
}
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
    messages
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
}
<websocket-message-broker>
    <intercept-message pattern="/user/queue/errors" access="permitAll"/>
    <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>

应更改为:Spring中文文档

@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
    messages
        .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        .anyMessage().denyAll();
}
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
    messages
        .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        .anyMessage().denyAll()
}
<websocket-message-broker>
    <intercept-message type="CONNECT" access="permitAll"/>
    <intercept-message type="DISCONNECT" access="permitAll"/>
    <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
    <intercept-message pattern="/user/queue/errors" access="permitAll"/>
    <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
    <intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>

@EnableWebSocketSecurity

如果要禁用 CSRF 并且使用的是 Java 配置,则迁移步骤略有不同。 而不是使用 ,您将覆盖自己的相应方法。 有关此步骤的详细信息,请参阅参考手册@EnableWebSocketSecurityWebSocketMessageBrokerConfigurerSpring中文文档

如果您使用的是 Java 配置,请将@EnableWebSocketSecurity添加到您的应用程序中。Spring中文文档

例如,您可以将其添加到 websocket 安全配置类中,如下所示:Spring中文文档

@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
	// ...
}
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
	// ...
}

这将使原型实例可用,以鼓励通过组合而不是扩展进行配置。MessageMatcherDelegatingAuthorizationManager.BuilderSpring中文文档

使用实例AuthorizationManager<Message<?>>

要开始使用 ,可以在 XML 中设置属性,也可以在 Java 中发布 。AuthorizationManageruse-authorization-managerAuthorizationManager<Message<?>>@BeanSpring中文文档

例如,以下应用程序配置:Spring中文文档

@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
    messages
        .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        .anyMessage().denyAll();
}
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
    messages
        .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        .anyMessage().denyAll()
}
<websocket-message-broker>
    <intercept-message type="CONNECT" access="permitAll"/>
    <intercept-message type="DISCONNECT" access="permitAll"/>
    <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
    <intercept-message pattern="/user/queue/errors" access="permitAll"/>
    <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
    <intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>

更改为:Spring中文文档

@Bean
AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
    messages
        .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        .anyMessage().denyAll();
	return messages.build();
}
@Bean
fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
    messages
        .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        .anyMessage().denyAll()
    return messages.build()
}
<websocket-message-broker use-authorization-manager="true">
    <intercept-message type="CONNECT" access="permitAll"/>
    <intercept-message type="DISCONNECT" access="permitAll"/>
    <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
    <intercept-message pattern="/user/queue/errors" access="permitAll"/>
    <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
    <intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>

停止实施AbstractSecurityWebSocketMessageBrokerConfigurer

如果您使用的是 Java 配置,您现在可以简单地扩展 .WebSocketMessageBrokerConfigurerSpring中文文档

例如,如果扩展的类称为 ,则:AbstractSecurityWebSocketMessageBrokerConfigurerWebSocketSecurityConfigSpring中文文档

@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
	// ...
}
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
	// ...
}

更改为:Spring中文文档

@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
	// ...
}
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
	// ...
}

选择退出步骤

如果您遇到问题,请查看以下方案,以获得最佳选择退出行为:Spring中文文档

我无法为所有请求声明授权规则

如果您在设置授权规则时遇到问题,请改用 permitAll,如下所示:anyRequestdenyAllSpring中文文档

@Bean
AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
    messages
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        // ...
        .anyMessage().permitAll();
	return messages.build();
}
@Bean
fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
    messages
        .simpDestMatchers("/user/queue/errors").permitAll()
        .simpDestMatchers("/admin/**").hasRole("ADMIN")
        // ...
        .anyMessage().permitAll();
    return messages.build()
}
<websocket-message-broker use-authorization-manager="true">
    <intercept-message pattern="/user/queue/errors" access="permitAll"/>
    <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
    <!-- ... -->
    <intercept-message pattern="/**" access="permitAll"/>
</websocket-message-broker>

我无法让 CSRF 工作,需要一些其他功能,或者遇到问题AbstractSecurityWebSocketMessageBrokerConfigurerAuthorizationManager

对于 Java,您可以继续使用 . 即使它已被弃用,也不会在 6.0 中删除它。AbstractMessageSecurityWebSocketMessageBrokerConfigurerSpring中文文档

对于 XML,您可以通过设置以下设置来选择退出:AuthorizationManageruse-authorization-manager="false"Spring中文文档

Xml格式
<websocket-message-broker>
    <intercept-message pattern="/user/queue/errors" access="permitAll"/>
    <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>
Xml格式
<websocket-message-broker use-authorization-manager="false">
    <intercept-message pattern="/user/queue/errors" access="permitAll"/>
    <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>

如果要禁用 CSRF 并且使用的是 Java 配置,则迁移步骤略有不同。 而不是使用 ,您将覆盖自己的相应方法。 有关此步骤的详细信息,请参阅参考手册@EnableWebSocketSecurityWebSocketMessageBrokerConfigurerSpring中文文档

用于请求安全性AuthorizationManager

如果您在进行这些更改时遇到问题,可以按照本节末尾的选择退出步骤进行操作。Spring中文文档

确保所有请求都定义了授权规则

在 Spring Security 5.8 及更早版本中,默认情况下允许没有授权规则的请求。 默认情况下拒绝是一种更强的安全状态,因此需要为每个端点明确定义授权规则。 因此,在 6.0 中,Spring Security 默认拒绝任何缺少授权规则的请求。Spring中文文档

准备此更改的最简单方法是引入适当的 anyRequest 规则作为最后的授权规则。 建议是 denyAll,因为这是隐含的 6.0 默认值。Spring中文文档

您可能已经定义了一个令您满意的规则,在这种情况下,可以跳过此步骤。anyRequestSpring中文文档

添加到末尾看起来像是改变:denyAllSpring中文文档

http
    .authorizeRequests((authorize) -> authorize
        .filterSecurityInterceptorOncePerRequest(true)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
    )
    // ...
http {
    authorizeRequests {
        filterSecurityInterceptorOncePerRequest = true
        authorize("/app/**", hasRole("APP"))
        // ...
    }
}
<http once-per-request="true">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
</http>
http
    .authorizeRequests((authorize) -> authorize
        .filterSecurityInterceptorOncePerRequest(true)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeRequests {
        filterSecurityInterceptorOncePerRequest = true
        authorize("/app/**", hasRole("APP"))
        // ...
        authorize(anyRequest, denyAll)
    }
}
<http once-per-request="true">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
    <intercept-url pattern="/**" access="denyAll"/>
</http>

如果已迁移到 ,则建议的更改是相同的。authorizeHttpRequestsSpring中文文档

切换到AuthorizationManager

要选择使用 ,您可以分别对 Java 或 XML 使用 或 use-authorization-managerAuthorizationManagerauthorizeHttpRequestsSpring中文文档

http
    .authorizeRequests((authorize) -> authorize
        .filterSecurityInterceptorOncePerRequest(true)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeRequests {
        filterSecurityInterceptorOncePerRequest = true
        authorize("/app/**", hasRole("APP"))
        // ...
        authorize(anyRequest, denyAll)
    }
}
<http once-per-request="true">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
    <intercept-url pattern="/**" access="denyAll"/>
</http>
http
    .authorizeHttpRequests((authorize) -> authorize
        .shouldFilterAllDispatcherTypes(false)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeHttpRequests {
        shouldFilterAllDispatcherTypes = false
        authorize("/app/**", hasRole("APP"))
        // ...
        authorize(anyRequest, denyAll)
    }
}
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
    <intercept-url pattern="/**" access="denyAll"/>
</http>

迁移到hasIpAddressaccess(AuthorizationManager)

hasIpAddress中没有 DSL 等效项。authorizeHttpRequestsSpring中文文档

因此,您需要将任何调用更改为使用 .hasIpAddressAuthorizationManagerSpring中文文档

首先,构造一个这样的:IpAddressMatcherSpring中文文档

爪哇岛
IpAddressMatcher hasIpAddress = new IpAddressMatcher("127.0.0.1");

然后从这里改变:Spring中文文档

爪哇岛
http
    .authorizeRequests((authorize) -> authorize
        .mvcMatchers("/app/**").hasIpAddress("127.0.0.1")
        // ...
        .anyRequest().denyAll()
    )
    // ...
爪哇岛
http
    .authorizeHttpRequests((authorize) -> authorize
        .requestMatchers("/app/**").access((authentication, context) ->
            new AuthorizationDecision(hasIpAddress.matches(context.getRequest()))
        // ...
        .anyRequest().denyAll()
    )
    // ...
通过IP地址保护首先是相当脆弱的。 因此,没有计划将此支持移植到 。authorizeHttpRequests

将 SpEL 表达式迁移到AuthorizationManager

对于授权规则,Java 往往比 SpEL 更容易测试和维护。 因此,没有用于声明 SpEL 的方法。authorizeHttpRequestsStringSpring中文文档

相反,您可以实现自己的实现或使用 .AuthorizationManagerWebExpressionAuthorizationManagerSpring中文文档

为了完整起见,将演示这两个选项。Spring中文文档

首先,如果您具有以下 SpEL:Spring中文文档

http
    .authorizeRequests((authorize) -> authorize
        .filterSecurityInterceptorOncePerRequest(true)
        .mvcMatchers("/complicated/**").access("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeRequests {
        filterSecurityInterceptorOncePerRequest = true
        authorize("/complicated/**", access("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
        // ...
        authorize(anyRequest, denyAll)
    }
}

然后,你可以用 Spring Security 授权原语编写自己的原语,如下所示:AuthorizationManagerSpring中文文档

http
    .authorizeHttpRequests((authorize) -> authorize
        .shouldFilterAllDispatcherTypes(false)
        .mvcMatchers("/complicated/**").access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeHttpRequests {
        shouldFilterAllDispatcherTypes = false
        authorize("/complicated/**", access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
        // ...
        authorize(anyRequest, denyAll)
    }
}

或者,您可以通过以下方式使用:WebExpressionAuthorizationManagerSpring中文文档

http
    .authorizeRequests((authorize) -> authorize
        .filterSecurityInterceptorOncePerRequest(true)
        .mvcMatchers("/complicated/**").access(
			new WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
        )
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeRequests {
        filterSecurityInterceptorOncePerRequest = true
        authorize("/complicated/**", access(
            WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
        )
        // ...
        authorize(anyRequest, denyAll)
    }
}

切换以筛选所有调度程序类型

Spring Security 5.8 及更早版本对每个请求仅执行一次授权。 这意味着默认情况下,调度程序类型(如 和 运行后)不受保护。FORWARDINCLUDEREQUESTSpring中文文档

建议 Spring Security 保护所有调度类型。 因此,在 6.0 中,Spring Security 更改了此默认值。Spring中文文档

因此,最后,更改授权规则以过滤所有调度程序类型。Spring中文文档

为此,您应该更改:Spring中文文档

http
    .authorizeHttpRequests((authorize) -> authorize
        .shouldFilterAllDispatcherTypes(false)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeHttpRequests {
        shouldFilterAllDispatcherTypes = false
        authorize("/app/**", hasRole("APP"))
        // ...
        authorize(anyRequest, denyAll)
    }
}
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
    <intercept-url pattern="/**" access="denyAll"/>
</http>
http
    .authorizeHttpRequests((authorize) -> authorize
        .shouldFilterAllDispatcherTypes(true)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeHttpRequests {
        shouldFilterAllDispatcherTypes = true
        authorize("/app/**", hasRole("APP"))
        // ...
        authorize(anyRequest, denyAll)
    }
}
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
    <intercept-url pattern="/**" access="denyAll"/>
</http>

并且,还应该为所有调度程序类型注册。 如果您使用的是 Spring Boot,则必须更改 spring.security.filter.dispatcher-types 属性以包含所有调度程序类型:FilterChainProxySpring中文文档

spring.security.filter.dispatcher-types=request,async,error,forward,include

如果使用的是 AbstractSecurityWebApplicationInitializer,则应重写该方法并返回所有调度程序类型:getSecurityDispatcherTypesSpring中文文档

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    @Override
    protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
        return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC,
                DispatcherType.FORWARD, DispatcherType.INCLUDE);
    }

}

使用 Spring MVC 时允许FORWARD

如果使用 Spring MVC 解析视图名称,则需要允许请求。 这是因为当 Spring MVC 检测到视图名称和实际视图之间的映射时,它将执行对视图的转发。 正如我们在上一节中看到的,Spring Security 6.0 默认情况下会对请求应用授权。FORWARDFORWARDSpring中文文档

请考虑以下常见配置:Spring中文文档

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .shouldFilterAllDispatcherTypes(true)
            .requestMatchers("/").authenticated()
            .anyRequest().denyAll()
        )
        .formLogin((form) -> form
            .loginPage("/login")
            .permitAll()
        ));
    return http.build();
}

以及以下等效的 MVC 视图映射配置之一:Spring中文文档

@Controller
public class MyController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }

}
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }

}

无论使用哪种配置,当有请求时,Spring MVC 将执行转发到视图,该视图使用默认配置,位于 path 下。 安全配置允许请求,但所有其他请求都将被拒绝,包括对 下的视图的请求。/loginloginsrc/main/resources/templates/login.html/loginFORWARD/templates/login.htmlSpring中文文档

要解决此问题,您应该将 Spring Security 配置为允许请求:FORWARDSpring中文文档

http
    .authorizeHttpRequests((authorize) -> authorize
        .shouldFilterAllDispatcherTypes(true)
        .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeHttpRequests {
        shouldFilterAllDispatcherTypes = true
        authorize(DispatcherTypeRequestMatcher(DispatcherType.FORWARD), permitAll)
        authorize(anyRequest, denyAll)
    }
}
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
    <intercept-url request-matcher-ref="forwardRequestMatcher" access="permitAll()" />
    <!-- ... -->
    <intercept-url pattern="/**" access="denyAll"/>
</http>

<bean name="forwardRequestMatcher" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
    <constructor-arg value="FORWARD"/>
</bean>

替换任何自定义过滤器安全AccessDecisionManager

应用程序可能具有自定义的 AccessDecisionManagerAccessDecisionVoter 排列。 准备策略将取决于您进行每次安排的原因。 请继续阅读以找到最适合您情况的匹配。Spring中文文档

我使用UnanimousBased

如果你的应用程序使用 UnanimousBased,你应该首先调整或替换任何 s,然后你可以构造一个这样的:AccessDecisionVoterAuthorizationManagerSpring中文文档

@Bean
AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
    PolicyAuthorizationManager policy = ...;
    LocalAuthorizationManager local = ...;
    return AuthorizationManagers.allOf(policy, local);
}
@Bean
fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
    val policy: PolicyAuthorizationManager = ...
    val local: LocalAuthorizationManager = ...
    return AuthorizationManagers.allOf(policy, local)
}
<bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
        factory-method="allOf">
    <constructor-arg>
        <util:list>
            <bean class="my.PolicyAuthorizationManager"/>
            <bean class="my.LocalAuthorizationManager"/>
        </util:list>
    </constructor-arg>
</bean>

然后,将其连接到 DSL,如下所示:Spring中文文档

http
    .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
    // ...
http {
    authorizeHttpRequests {
        authorize(anyRequest, requestAuthorization)
    }
    // ...
}
<http authorization-manager-ref="requestAuthorization"/>

authorizeHttpRequests旨在将自定义应用于任何 URL 模式。 有关详细信息,请参阅参考AuthorizationManagerSpring中文文档

我使用AffirmativeBased

如果应用程序使用 AffirmativeBased,则可以构造等效的 AuthorizationManager,如下所示:Spring中文文档

@Bean
AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
    PolicyAuthorizationManager policy = ...;
    LocalAuthorizationManager local = ...;
    return AuthorizationManagers.anyOf(policy, local);
}
@Bean
fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
    val policy: PolicyAuthorizationManager = ...
    val local: LocalAuthorizationManager = ...
    return AuthorizationManagers.anyOf(policy, local)
}
<bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
        factory-method="anyOf">
    <constructor-arg>
        <util:list>
            <bean class="my.PolicyAuthorizationManager"/>
            <bean class="my.LocalAuthorizationManager"/>
        </util:list>
    </constructor-arg>
</bean>

然后,将其连接到 DSL,如下所示:Spring中文文档

http
    .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
    // ...
http {
    authorizeHttpRequests {
        authorize(anyRequest, requestAuthorization)
    }
    // ...
}
<http authorization-manager-ref="requestAuthorization"/>

authorizeHttpRequests旨在将自定义应用于任何 URL 模式。 有关详细信息,请参阅参考AuthorizationManagerSpring中文文档

我使用ConsensusBased

没有框架提供的等效项。 在这种情况下,请实现一个复合 AuthorizationManager,该组合将委托 s 集考虑在内。AuthorizationManagerSpring中文文档

实现后,请按照参考手册中的详细信息添加自定义 AuthorizationManagerAuthorizationManagerSpring中文文档

我使用自定义AccessDecisionVoter

应更改类以实现 AuthorizationManager 或创建适配器。Spring中文文档

如果不知道您的自定义投票者在做什么,就不可能推荐通用解决方案。 举个例子,下面是调整 SecurityMetadataSourceAccessDecisionVoter 的样子:anyRequest().authenticated()Spring中文文档

public final class AnyRequestAuthenticatedAuthorizationManagerAdapter implements AuthorizationManager<RequestAuthorizationContext> {
    private final SecurityMetadataSource metadata;
    private final AccessDecisionVoter voter;

    public PreAuthorizeAuthorizationManagerAdapter(SecurityExpressionHandler expressionHandler) {
        Map<RequestMatcher, List<ConfigAttribute>> requestMap = Collections.singletonMap(
                AnyRequestMatcher.INSTANCE, Collections.singletonList(new SecurityConfig("authenticated")));
        this.metadata = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
        WebExpressionVoter voter = new WebExpressionVoter();
        voter.setExpressionHandler(expressionHandler);
        this.voter = voter;
    }

    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
        List<ConfigAttribute> attributes = this.metadata.getAttributes(context);
        int decision = this.voter.vote(authentication.get(), invocation, attributes);
        if (decision == ACCESS_GRANTED) {
            return new AuthorizationDecision(true);
        }
        if (decision == ACCESS_DENIED) {
            return new AuthorizationDecision(false);
        }
        return null; // abstain
    }
}

实现后,请按照参考手册中的详细信息添加自定义 AuthorizationManagerAuthorizationManagerSpring中文文档

如果使用hasRolehasAuthorityGrantedAuthorityDefaults

目前,里面的方法不像 bean 那样支持 bean。 因此,如果要用于更改角色的前缀,则需要使用 而不是 .hasRoleauthorizeHttpRequestsGrantedAuthorityDefaultsauthorizeRequestsGrantedAuthorityDefaultshasAuthorityhasRoleSpring中文文档

例如,您必须从以下位置更改:Spring中文文档

具有自定义角色前缀的 authorizeRequests
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeRequests((authorize) -> authorize
            .anyRequest().hasRole("ADMIN")
        );
    return http.build();
}

@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
    return new GrantedAuthorityDefaults("MYPREFIX_");
}
authorizeHttpRequests with hasAuthority 和自定义角色前缀
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().hasAuthority("MYPREFIX_ADMIN")
        );
    return http.build();
}

将来应该支持此功能,有关详细信息,请参阅 gh-13227Spring中文文档

选择退出步骤

如果您遇到问题,请查看以下方案,以获得最佳选择退出行为:Spring中文文档

我无法保护所有调度程序类型

如果无法保护所有调度程序类型,请首先尝试声明哪些调度程序类型不需要授权,如下所示:Spring中文文档

http
    .authorizeHttpRequests((authorize) -> authorize
        .shouldFilterAllDispatcherTypes(true)
        .dispatcherTypeMatchers(FORWARD, INCLUDE).permitAll()
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
        .anyRequest().denyAll()
    )
    // ...
http {
    authorizeHttpRequests {
        shouldFilterAllDispatcherTypes = true
        authorize(DispatcherTypeRequestMatcher(FORWARD, INCLUDE), permitAll)
        authorize("/app/**", hasRole("APP"))
        // ...
        authorize(anyRequest, denyAll)
    }
}
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
    <intercept-url request-matcher-ref="dispatchers"/>
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
    <intercept-url pattern="/**" access="denyAll"/>
</http>

<bean id="dispatchers" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
    <constructor-arg>
        <util:list value-type="javax.servlet.DispatcherType">
            <value>FORWARD</value>
            <value>INCLUDE</value>
        </util:list>
    </constructor-arg>
</bean>

或者,如果这不起作用,则可以通过设置和以下方式显式选择退出该行为:filter-all-dispatcher-typesfilterAllDispatcherTypesfalseSpring中文文档

http
    .authorizeHttpRequests((authorize) -> authorize
        .filterAllDispatcherTypes(false)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
    )
    // ...
http {
    authorizeHttpRequests {
        filterAllDispatcherTypes = false
        authorize("/messages/**", hasRole("APP"))
        // ...
    }
}
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
</http>

或者,如果您仍在使用 或 ,则设置为:authorizeRequestsuse-authorization-manager="false"oncePerRequesttrueSpring中文文档

http
    .authorizeRequests((authorize) -> authorize
        .filterSecurityInterceptorOncePerRequest(true)
        .mvcMatchers("/app/**").hasRole("APP")
        // ...
    )
    // ...
http {
    authorizeRequests {
        filterSecurityInterceptorOncePerRequest = true
        authorize("/messages/**", hasRole("APP"))
        // ...
    }
}
<http once-per-request="true" use-authorization-manager="false">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
</http>

我无法为所有请求声明授权规则

如果您在设置授权规则时遇到问题,请改用 permitAll,如下所示:anyRequestdenyAllSpring中文文档

http
    .authorizeHttpReqeusts((authorize) -> authorize
        .mvcMatchers("/app/*").hasRole("APP")
        // ...
        .anyRequest().permitAll()
    )
http {
    authorizeHttpRequests {
        authorize("/app*", hasRole("APP"))
        // ...
        authorize(anyRequest, permitAll)
    }
}
<http>
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
    <intercept-url pattern="/**" access="permitAll"/>
</http>

我无法迁移我的 SpEL 或我的AccessDecisionManager

如果您在使用 SpEL 时遇到问题,或者您需要在 or 中继续使用其他一些功能,请尝试以下操作。AccessDecisionManager<http>authorizeRequestsSpring中文文档

首先,如果您仍然需要,欢迎您继续使用它。即使它已被弃用,也不会在 6.0 中删除它。authorizeRequestsSpring中文文档

其次,如果您仍然需要自定义或有其他原因选择退出,请执行:access-decision-manager-refAuthorizationManagerSpring中文文档

Xml格式
<http use-authorization-manager="false">
    <intercept-url pattern="/app/*" access="hasRole('APP')"/>
    <!-- ... -->
</http>

您可能已经定义了一个令您满意的规则,在这种情况下,可以跳过此步骤。anyRequestSpring中文文档

通过IP地址保护首先是相当脆弱的。 因此,没有计划将此支持移植到 。authorizeHttpRequests

authorizeHttpRequests旨在将自定义应用于任何 URL 模式。 有关详细信息,请参阅参考AuthorizationManagerSpring中文文档

authorizeHttpRequests旨在将自定义应用于任何 URL 模式。 有关详细信息,请参阅参考AuthorizationManagerSpring中文文档