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

授权架构

本节描述了适用于授权的 Spring Security 体系结构。spring-doc.cn

当局

Authentication 讨论了所有 implementations 如何存储对象列表。 这些权限表示已授予委托人的权限。 对象由 插入到对象中,稍后在做出授权决策时由实例读取。AuthenticationGrantedAuthorityGrantedAuthorityAuthenticationAuthenticationManagerAccessDecisionManagerspring-doc.cn

该接口只有一个方法:GrantedAuthorityspring-doc.cn

String getAuthority();

此方法允许实例获取 . 通过将表示形式返回为 a ,大多数实现都可以轻松 “读取” a。 如果 a 不能精确表示为 a ,则认为 为 “复杂” ,必须返回 。AccessDecisionManagerStringGrantedAuthorityStringGrantedAuthorityAccessDecisionManagerGrantedAuthorityStringGrantedAuthoritygetAuthority()nullspring-doc.cn

“复杂”的一个示例是存储适用于不同客户账号的操作和权限阈值列表的实现。 将这个复合体表示为 a 将非常困难。因此,该方法应返回 . 这向任何人表明它需要支持特定的实现才能理解其内容。GrantedAuthorityGrantedAuthorityStringgetAuthority()nullAccessDecisionManagerGrantedAuthorityspring-doc.cn

Spring Security 包括一个具体的实现:. 此实现允许将任何用户指定的 . 安全架构中包含的所有实例都用于填充对象。GrantedAuthoritySimpleGrantedAuthorityStringGrantedAuthorityAuthenticationProviderSimpleGrantedAuthorityAuthenticationspring-doc.cn

调用前处理

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

The AuthorizationManager

自定义或建议更改为使用 AuthorizationManager 的应用程序。AccessDecisionManagerAccessDecisionVoterspring-doc.cn

AuthorizationManagerAuthorizationFilter 调用,并负责做出最终的访问控制决策。 该接口包含两种方法:AuthorizationManagerspring-doc.cn

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

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

的方法 将传递它需要的所有相关信息,以便做出授权决策。 特别是,传递 secure 可以检查实际 secure 对象调用中包含的那些参数。 例如,假设安全对象是一个 . 查询 the 以查找任何参数都很容易,然后在 中实现某种安全逻辑,以确保允许主体对该客户进行操作。 如果授予访问权限,则实现应返回正值,如果拒绝访问,则返回负值,并在放弃决策时返回 null。AuthorizationManagercheckObjectMethodInvocationMethodInvocationCustomerAuthorizationManagerAuthorizationDecisionAuthorizationDecisionAuthorizationDecisionspring-doc.cn

verify调用,随后在否定的情况下抛出 an。checkAccessDeniedExceptionAuthorizationDecisionspring-doc.cn

基于委托的 AuthorizationManager 实现

虽然用户可以实现自己的 delegating 来控制授权的所有方面,但 Spring Security 附带了一个可以与单个 协作的委托。AuthorizationManagerAuthorizationManagerAuthorizationManagerspring-doc.cn

RequestMatcherDelegatingAuthorizationManager将请求与最合适的委托 匹配。 为了方法安全性,您可以使用 和 。AuthorizationManagerAuthorizationManagerBeforeMethodInterceptorAuthorizationManagerAfterMethodInterceptorspring-doc.cn

authorizationhierarchy
图 1.授权管理器实现

使用此方法,可以在授权决策时轮询实施组合。AuthorizationManagerspring-doc.cn

权限授权管理器

Spring Security 提供的最常见是 。 它配置了一组给定的权限,以在当前 . 如果 包含任何已配置的 authority,它将返回 positive。 否则,它将返回负数。AuthorizationManagerAuthorityAuthorizationManagerAuthenticationAuthorizationDecisionAuthenticationAuthorizationDecisionspring-doc.cn

已验证的授权管理器

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

自定义授权管理器

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

您可以在 Spring 网站上找到一篇博客文章,其中描述了如何使用旧版实时拒绝帐户被暂停的用户的访问。 您可以通过改为实施来实现相同的结果。AccessDecisionVoterAuthorizationManager

调整 AccessDecisionManager 和 AccessDecisionVoters

在 之前,Spring Security 发布了 AccessDecisionManagerAccessDecisionVoterAuthorizationManagerspring-doc.cn

在某些情况下,例如迁移较旧的应用程序,可能需要引入调用 或 的 an 。AuthorizationManagerAccessDecisionManagerAccessDecisionVoterspring-doc.cn

要调用现有 ,您可以执行以下操作:AccessDecisionManagerspring-doc.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);
    }
}

然后将其连接到您的 .SecurityFilterChainspring-doc.cn

或者只调用 an ,你可以这样做:AccessDecisionVoterspring-doc.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;
    }
}

然后将其连接到您的 .SecurityFilterChainspring-doc.cn

分层角色

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

使用 role-hierarchy 允许您配置哪些角色(或权限)应包含其他角色。 Spring Security 的 , , 的扩展版本配置了 ,从中它可以获取分配给用户的所有 “可访问的权限”。 典型的配置可能如下所示:RoleVoterRoleHierarchyVoterRoleHierarchyspring-doc.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 配置尚未移植到 . 因此,此示例使用 . 如果您需要方法安全性支持,请继续使用,直到 github.com/spring-projects/spring-security/issues/12783 完成。@EnableMethodSecurityAccessDecisionVoterRoleHierarchy@EnableGlobalMethodSecurity

在这里,我们在层次结构中有四个角色。 使用 进行身份验证的用户在根据适应调用上述 . 该符号可以被认为是“包含”的意思。ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUESTROLE_ADMINAuthorizationManagerRoleHierarchyVoter>spring-doc.cn

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

旧版授权组件

Spring Security 包含一些旧组件。 由于它们尚未删除,因此包含文档以用于历史目的。 他们推荐的替代品如上所述。

The AccessDecisionManager

由 调用,并负责做出最终的访问控制决策。 该接口包含三种方法:AccessDecisionManagerAbstractSecurityInterceptorAccessDecisionManagerspring-doc.cn

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

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

的方法将传递做出授权决策所需的所有相关信息。 特别是,传递 secure 可以检查实际 secure 对象调用中包含的那些参数。 例如,假设安全对象是 . 您可以查询 for any 参数,然后在 中实现某种安全逻辑,以确保允许委托人对该客户进行操作。 实现应引发 if 访问被拒绝。decideAccessDecisionManagerObjectMethodInvocationMethodInvocationCustomerAccessDecisionManagerAccessDeniedExceptionspring-doc.cn

该方法由在启动时调用,以确定是否可以处理传递的 . 该方法由安全侦听器实现调用,以确保配置的支持安全侦听器提供的安全对象的类型。supports(ConfigAttribute)AbstractSecurityInterceptorAccessDecisionManagerConfigAttributesupports(Class)AccessDecisionManagerspring-doc.cn

基于投票的 AccessDecisionManager 实现

虽然用户可以实现自己的实现来控制授权的所有方面,但 Spring Security 包括几个基于投票的实现。Voting Decision Manager 描述了相关的类。AccessDecisionManagerAccessDecisionManagerspring-doc.cn

下图显示了界面:AccessDecisionManagerspring-doc.cn

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

通过使用此方法,将对授权决策进行一系列实施轮询。 然后,它根据其对选票的评估来决定是否投掷一个。AccessDecisionVoterAccessDecisionManagerAccessDeniedExceptionspring-doc.cn

该接口有三种方法:AccessDecisionVoterspring-doc.cn

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

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

具体实现返回 , 可能的值反映在名为 、 和 的静态字段中。 如果投票实现对授权决策没有意见,则返回。 如果它确实有观点,则必须返回 或 。intAccessDecisionVoterACCESS_ABSTAINACCESS_DENIEDACCESS_GRANTEDACCESS_ABSTAINACCESS_DENIEDACCESS_GRANTEDspring-doc.cn

Spring Security 提供了三种具体的实现来统计选票。 该实现根据非弃权票的共识授予或拒绝访问权限。 提供属性以控制在票数相等或所有投票都弃权的情况下的行为。 如果收到一个或多个投票,则实现将授予访问权限(换句话说,如果至少有一个授权投票,则将忽略拒绝投票)。 与实现一样,有一个参数控制所有选民弃权时的行为。 提供商希望获得一致投票才能授予访问权限,而忽略弃权票。 如果有任何投票,它就会拒绝访问。 与其他实现一样,有一个参数控制所有选民弃权时的行为。AccessDecisionManagerConsensusBasedAffirmativeBasedACCESS_GRANTEDConsensusBasedUnanimousBasedACCESS_GRANTEDACCESS_DENIEDspring-doc.cn

您可以实施以不同方式计算选票的自定义。 例如,来自特定选民的投票可能会获得额外的权重,而来自特定选民的拒绝投票可能具有否决权。AccessDecisionManagerAccessDecisionVoterspring-doc.cn

角色投票者

Spring Security 提供的最常用的是 ,它将配置属性视为角色名称,如果已为用户分配了该角色,则投票授予访问权限。AccessDecisionVoterRoleVoterspring-doc.cn

如果 any 以前缀开头,则进行投票。 如果存在 a 返回的表示形式(来自方法)正好等于一个或多个以前缀开头的表示形式,则它会投票授予访问权限。 如果没有任何以 开头的 的完全匹配项,则投票拒绝访问。 如果 no 开头为 ,则选民弃权。ConfigAttributeROLE_GrantedAuthorityStringgetAuthority()ConfigAttributesROLE_ConfigAttributeROLE_RoleVoterConfigAttributeROLE_spring-doc.cn

已验证的选民

我们隐式看到的另一个投票者是 ,它可以用来区分匿名用户、完全验证的用户和经过验证的记住我的用户。 许多站点允许在 remember-me 身份验证下进行某些受限访问,但需要用户通过登录来确认其身份才能获得完全访问权限。AuthenticatedVoterspring-doc.cn

当我们使用该属性授予匿名访问权限时,此属性由 . 有关更多信息,请参阅 AuthenticatedVoterIS_AUTHENTICATED_ANONYMOUSLYAuthenticatedVoterspring-doc.cn

自定义选民

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

调用后
图 3.调用后实现

与 Spring Security 的许多其他部分一样,有一个具体的实现,它轮询 s 的列表。 每个 API 都可以修改 return 对象或抛出 . 事实上,多个提供程序可以修改对象,因为前一个提供程序的结果会传递给列表中的下一个提供程序。AfterInvocationManagerAfterInvocationProviderManagerAfterInvocationProviderAfterInvocationProviderAccessDeniedExceptionspring-doc.cn

请注意,如果您使用的是 ,您仍然需要允许 允许 操作的配置属性。 如果您使用的是典型的 Spring Security 包含的实现,则没有为特定的安全方法调用定义配置属性将导致每个实现都放弃投票。 反过来,如果属性 “allowIfAllAbstainDecisions” 为 ,则会引发 an。 您可以通过 (i) 将“allowIfAllAbstainDecisions”设置为(尽管通常不建议这样做)或 (ii) 简单地确保至少有一个配置属性可供投票授予访问权限来避免此潜在问题。 后一种(推荐)方法通常是通过 or 配置属性实现的。AfterInvocationManagerMethodSecurityInterceptorAccessDecisionManagerAccessDecisionManagerAccessDecisionVoterAccessDecisionManagerfalseAccessDeniedExceptiontrueAccessDecisionVoterROLE_USERROLE_AUTHENTICATEDspring-doc.cn