对于最新的稳定版本,请使用 Spring Security 6.5.3spring-doc.cadn.net.cn

授权架构

本节介绍适用于授权的 Spring Security 体系结构。spring-doc.cadn.net.cn

当局

Authentication讨论了如何Authentication实现存储一个列表GrantedAuthority对象。 这些代表已授予委托人的权限。 这GrantedAuthority对象入到Authentication对象AuthenticationManager后来被读取AccessDecisionManager实例。spring-doc.cadn.net.cn

GrantedAuthorityinterface 只有一种方法:spring-doc.cadn.net.cn

String getAuthority();

此方法允许AccessDecisionManager实例以获得精确的String的表示GrantedAuthority. 通过将表示形式返回为String一个GrantedAuthority大多数人都能轻松“阅读”AccessDecisionManager实现。 如果GrantedAuthority不能精确地表示为StringGrantedAuthority被认为是“复杂”的,并且getAuthority()必须返回null.spring-doc.cadn.net.cn

“复合体”的例子GrantedAuthority将是一个实现,用于存储适用于不同客户帐号的作和权限阈值列表。 代表这个综合体GrantedAuthority作为String会很困难。因此,getAuthority()方法应返回null. 这表示任何AccessDecisionManager它需要支持特定的GrantedAuthority实现以理解其内容。spring-doc.cadn.net.cn

Spring Security 包括一个混凝土GrantedAuthority实现:SimpleGrantedAuthority. 此实现允许任何用户指定的String转换为GrantedAuthority. 都AuthenticationProvider安全架构中包含的实例使用SimpleGrantedAuthority以填充Authentication对象。spring-doc.cadn.net.cn

调用前处理

Spring Security 提供了控制对安全对象(例如方法调用或 Web 请求)的访问的拦截器。 关于是否允许调用继续进行的调用前决定由AccessDecisionManager.spring-doc.cadn.net.cn

授权管理器

自定义AccessDecisionManagerAccessDecisionVoter鼓励更改为AuthorizationManager.spring-doc.cadn.net.cn

AuthorizationManagers 由AuthorizationFilter并负责做出最终的访问控制决策。 这AuthorizationManager接口包含两个方法:spring-doc.cadn.net.cn

AuthorizationDecision check(Supplier<Authentication> authentication, Object secureObject);

default AuthorizationDecision verify(Supplier<Authentication> authentication, Object secureObject)
        throws AccessDeniedException {
    // ...
}

AuthorizationManagercheck方法会传递它所需的所有相关信息,以便做出授权决策。 特别是,通过安全的Object允许检查实际安全对象调用中包含的那些参数。 例如,假设安全对象是MethodInvocation. 查询MethodInvocation对于任何Customer参数,然后在AuthorizationManager以确保允许委托人对该客户进行作。 预计实现将返回积极的AuthorizationDecision如果授予访问权限,则为否定AuthorizationDecision如果访问被拒绝,并且AuthorizationDecision当放弃做出决定时。spring-doc.cadn.net.cn

verify调用check并随后抛出一个AccessDeniedException在负的情况下AuthorizationDecision.spring-doc.cadn.net.cn

基于委托的 AuthorizationManager 实现

虽然用户可以实现自己的AuthorizationManager为了控制授权的各个方面,Spring Security 附带了一个委托AuthorizationManager可以与个人协作AuthorizationManagers.spring-doc.cadn.net.cn

RequestMatcherDelegatingAuthorizationManager将请求与最合适的委托进行匹配AuthorizationManager. 为了方法安全性,您可以使用AuthorizationManagerBeforeMethodInterceptorAuthorizationManagerAfterMethodInterceptor.spring-doc.cadn.net.cn

授权层次结构
图 1.授权管理器实现

使用这种方法,组合AuthorizationManager可以在授权决策上轮询实现。spring-doc.cadn.net.cn

权限授权管理器

最常见的AuthorizationManager与 Spring Security 一起提供的是AuthorityAuthorizationManager. 它配置了一组给定的权限,以便在当前Authentication. 它将返回正数AuthorizationDecision应该Authentication包含任何已配置的权限。 它将返回一个负数AuthorizationDecision否则。spring-doc.cadn.net.cn

AuthenticatedAuthorizationManager

另一个管理器是AuthenticatedAuthorizationManager. 它可用于区分匿名用户、完全身份验证用户和记住我身份验证用户。 许多网站允许在记住我身份验证下进行某些有限访问,但要求用户通过登录来确认其身份才能获得完全访问权限。spring-doc.cadn.net.cn

自定义授权管理器

显然,您还可以实现自定义AuthorizationManager而且你可以将任何你想要的访问控制逻辑放入其中。 它可能特定于您的应用程序(与业务逻辑相关),也可能实现一些安全管理逻辑。 例如,您可以创建一个可以查询 Open Policy Agent 或您自己的授权数据库的实现。spring-doc.cadn.net.cn

你会在 Spring 网站上找到一篇博客文章,其中描述了如何使用旧版AccessDecisionVoter以实时拒绝帐户被暂停的用户访问。 您可以通过实施AuthorizationManager相反。

调整 AccessDecisionManager 和 AccessDecisionVoters

之前AuthorizationManager,Spring Security 发布AccessDecisionManagerAccessDecisionVoter.spring-doc.cadn.net.cn

在某些情况下,例如迁移较旧的应用程序,可能需要引入AuthorizationManager调用AccessDecisionManagerAccessDecisionVoter.spring-doc.cadn.net.cn

调用现有的AccessDecisionManager,您可以执行以下作:spring-doc.cadn.net.cn

调整 AccessDecisionManager
@Component
public class AccessDecisionManagerAuthorizationManagerAdapter implements AuthorizationManager {
    private final AccessDecisionManager accessDecisionManager;
    private final SecurityMetadataSource securityMetadataSource;

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, Object object) {
        try {
            Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(object);
            this.accessDecisionManager.decide(authentication.get(), object, attributes);
            return new AuthorizationDecision(true);
        } catch (AccessDeniedException ex) {
            return new AuthorizationDecision(false);
        }
    }

    @Override
    public void verify(Supplier<Authentication> authentication, Object object) {
        Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(object);
        this.accessDecisionManager.decide(authentication.get(), object, attributes);
    }
}

然后将其连接到您的SecurityFilterChain.spring-doc.cadn.net.cn

或者只调用AccessDecisionVoter,您可以执行以下作:spring-doc.cadn.net.cn

调整 AccessDecisionVoter
@Component
public class AccessDecisionVoterAuthorizationManagerAdapter implements AuthorizationManager {
    private final AccessDecisionVoter accessDecisionVoter;
    private final SecurityMetadataSource securityMetadataSource;

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, Object object) {
        Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(object);
        int decision = this.accessDecisionVoter.vote(authentication.get(), object, attributes);
        switch (decision) {
        case ACCESS_GRANTED:
            return new AuthorizationDecision(true);
        case ACCESS_DENIED:
            return new AuthorizationDecision(false);
        }
        return null;
    }
}

然后将其连接到您的SecurityFilterChain.spring-doc.cadn.net.cn

分层角色

应用程序中的特定角色应自动“包含”其他角色,这是一个常见的要求。 例如,在具有“管理员”和“用户”角色概念的应用程序中,您可能希望管理员能够执行普通用户可以执行的所有作。 为此,您可以确保所有管理员用户也都分配了“用户”角色。 或者,您可以修改每个访问约束,这些限制要求“用户”角色也包括“管理员”角色。 如果您的应用程序中有很多不同的角色,这可能会变得非常复杂。spring-doc.cadn.net.cn

使用角色层次结构允许您配置哪些角色(或权限)应包括其他角色。 Spring Security 的RoleVoter,RoleHierarchyVoter,配置了RoleHierarchy,它从中获取分配给用户的所有“可访问权限”。 典型的配置可能如下所示:spring-doc.cadn.net.cn

分层角色配置
@Bean
AccessDecisionVoter hierarchyVoter() {
    RoleHierarchy hierarchy = new RoleHierarchyImpl();
    hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" +
            "ROLE_STAFF > ROLE_USER\n" +
            "ROLE_USER > ROLE_GUEST");
    return new RoleHierarchyVoter(hierarchy);
}
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
	<constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
		class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
	<property name="hierarchy">
		<value>
			ROLE_ADMIN > ROLE_STAFF
			ROLE_STAFF > ROLE_USER
			ROLE_USER > ROLE_GUEST
		</value>
	</property>
</bean>
RoleHierarchyBean 配置尚未移植到@EnableMethodSecurity. 因此,此示例使用AccessDecisionVoter. 如果您需要RoleHierarchy支持方法安全,请继续使用@EnableGlobalMethodSecurity直到 github.com/spring-projects/spring-security/issues/12783 完成。

这里,我们在层次结构中有四个角色ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST. 通过身份验证的用户ROLE_ADMIN,当根据AuthorizationManager适配调用上述RoleHierarchyVoter. 该符号可以被认为是“包含”的意思。>spring-doc.cadn.net.cn

角色层次结构提供了一种方便的方法来简化应用程序的访问控制配置数据和/或减少需要分配给用户的权限数量。 对于更复杂的要求,您可能希望在应用程序所需的特定访问权限和分配给用户的角色之间定义逻辑映射,在加载用户信息时在两者之间进行转换。spring-doc.cadn.net.cn

旧版授权组件

Spring Security 包含一些遗留组件。 由于它们尚未被删除,因此出于历史目的而包含文档。 他们推荐的替代品如上所示。

AccessDecisionManager 的

AccessDecisionManagerAbstractSecurityInterceptor并负责做出最终的访问控制决策。 这AccessDecisionManager接口包含三种方法:spring-doc.cadn.net.cn

void decide(Authentication authentication, Object secureObject,
	Collection<ConfigAttribute> attrs) throws AccessDeniedException;

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

decide方法AccessDecisionManager传递做出授权决策所需的所有相关信息。 特别是,通过安全的Object检查实际安全对象调用中包含的那些参数。 例如,假设安全对象是MethodInvocation. 您可以查询MethodInvocation对于任何Customer参数,然后在AccessDecisionManager以确保允许委托人对该客户进行作。 实现应抛出AccessDeniedException如果访问被拒绝。spring-doc.cadn.net.cn

supports(ConfigAttribute)方法由AbstractSecurityInterceptor在启动时确定AccessDecisionManager可以处理传递的ConfigAttribute. 这supports(Class)方法被安全拦截器实现调用,以确保配置的AccessDecisionManager支持安全拦截器呈现的安全对象类型。spring-doc.cadn.net.cn

基于投票的 AccessDecisionManager 实现

虽然用户可以实现自己的AccessDecisionManager为了控制授权的各个方面,Spring Security 包括几个AccessDecisionManager基于投票的实现。投票决策管理器描述了相关类。spring-doc.cadn.net.cn

下图显示了AccessDecisionManager接口:spring-doc.cadn.net.cn

访问决策投票
图 2.投票决策管理器

通过使用这种方法,一系列AccessDecisionVoter根据授权决策轮询实现。 这AccessDecisionManager然后决定是否抛出一个AccessDeniedException基于其对选票的评估。spring-doc.cadn.net.cn

AccessDecisionVoter接口有三种方法:spring-doc.cadn.net.cn

int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs);

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

具体实现返回int,可能的值反映在AccessDecisionVoter名为ACCESS_ABSTAIN,ACCESS_DENIEDACCESS_GRANTED. 投票实现返回ACCESS_ABSTAIN如果它对授权决定没有意见。 如果它确实有意见,它必须返回ACCESS_DENIEDACCESS_GRANTED.spring-doc.cadn.net.cn

有三种具体AccessDecisionManagerSpring Security 提供的实现来统计投票。 这ConsensusBased实施根据非弃权票的共识授予或拒绝访问权限。 提供属性来控制在票数相等或所有票都弃权的情况下的行为。 这AffirmativeBased如果一个或多个ACCESS_GRANTED收到了投票(换句话说,如果至少有一个授权投票,则拒绝投票将被忽略)。 像ConsensusBased实施时,如果所有选民都弃权,则有一个参数可以控制行为。 这UnanimousBased提供者期望一致ACCESS_GRANTED投票以授予访问权限,忽略弃权。 如果有任何访问,它会拒绝访问ACCESS_DENIED投票。 与其他实现一样,如果所有选民都弃权,则有一个参数来控制行为。spring-doc.cadn.net.cn

您可以实现自定义AccessDecisionManager统计选票的方式不同。 例如,来自特定AccessDecisionVoter可能会获得额外的权重,而特定选民的拒绝投票可能会产生否决权。spring-doc.cadn.net.cn

角色选民

最常用的AccessDecisionVoterSpring Security 提供的是RoleVoter,它将配置属性视为角色名称,并在用户被分配了该角色时投票授予访问权限。spring-doc.cadn.net.cn

如果有的话,它会投票ConfigAttributeROLE_前缀。 如果存在GrantedAuthority返回一个String表示形式(从getAuthority()方法)恰好等于一个或多个ConfigAttributesROLE_前缀。 如果没有任何完全匹配的ConfigAttribute开头ROLE_,RoleVoter投票拒绝访问。 如果没有ConfigAttribute开头为ROLE_,选民弃权。spring-doc.cadn.net.cn

已认证选民

我们隐含地看到的另一个选民是AuthenticatedVoter,可用于区分匿名用户、完全身份验证用户和记住我身份验证用户。 许多网站允许在记住我身份验证下进行某些有限的访问,但要求用户通过登录来确认其身份才能获得完全访问权限。spring-doc.cadn.net.cn

当我们使用IS_AUTHENTICATED_ANONYMOUSLY属性授予匿名访问权限,则此属性正在由AuthenticatedVoter. 有关更多信息,请参阅AuthenticatedVoter.spring-doc.cadn.net.cn

自定义选民

您还可以实现自定义AccessDecisionVoter并放入几乎任何您想要的访问控制逻辑。 它可能特定于您的应用程序(与业务逻辑相关),也可能实现一些安全管理逻辑。 例如,在 Spring 网站上,您可以找到一篇博客文章,其中描述了如何使用投票者实时拒绝帐户已被暂停的用户的访问。spring-doc.cadn.net.cn

调用后
图 3.调用实施后

与 Spring Security 的许多其他部分一样,AfterInvocationManager有一个单一的具体实现,AfterInvocationProviderManager,它轮询一个列表AfterInvocationProviders. 每AfterInvocationProvider允许修改返回对象或抛出AccessDeniedException. 事实上,多个提供程序可以修改对象,因为上一个提供程序的结果被传递给列表中的下一个提供程序。spring-doc.cadn.net.cn

请注意,如果您使用AfterInvocationManager,您仍然需要允许MethodSecurityInterceptorAccessDecisionManager以允许作。如果您使用的是典型的 Spring SecurityAccessDecisionManager实现,没有为特定安全方法调用定义配置属性将导致AccessDecisionVoter弃权。反过来,如果AccessDecisionManager属性“allowIfAllAbstainDecisions”是falseAccessDeniedException将被扔出。 您可以通过 (i) 将“allowIfAllAbstainDecisions”设置为true(尽管通常不建议这样做)或 (ii) 只需确保至少有一个配置属性AccessDecisionVoter将投票授予访问权限。 后一种(推荐的)方法通常通过ROLE_USERROLE_AUTHENTICATED配置属性。spring-doc.cadn.net.cn