On this page
24. Authorization Architecture
24.1 Authorities
正如我们在technical overview中看到的那样,所有Authentication
实现都存储了GrantedAuthority
个对象的列表。这些代表已授予委托人的权限。 GrantedAuthority
对象由AuthenticationManager
插入到Authentication
对象中,然后在做出授权决策时由AccessDecisionManager
读取。
GrantedAuthority
是只有一种方法的接口:
String getAuthority();
此方法允许AccessDecisionManager
获得GrantedAuthority
的精确String
表示。通过将表示形式返回为String
,大多数AccessDecisionManager
可以很容易地“读取” GrantedAuthority
。如果GrantedAuthority
不能精确地表示为String
,则GrantedAuthority
被视为“复杂”,并且getAuthority()
必须返回null
。
“复杂” GrantedAuthority
的示例是一种存储适用于不同 Client 帐号的操作和权限阈值列表的实现。将复杂的GrantedAuthority
表示为String
将会非常困难,因此getAuthority()
方法应返回null
。这将向任何AccessDecisionManager
表示需要特别支持GrantedAuthority
实现,以便理解其内容。
Spring Security 包含一个具体的GrantedAuthority
实现SimpleGrantedAuthority
。这允许将任何用户指定的String
转换为GrantedAuthority
。安全体系结构中包含的所有AuthenticationProvider
都使用SimpleGrantedAuthority
填充Authentication
对象。
24.2 调用前处理
正如我们在Technical Overview章中所看到的那样,Spring Security 提供了拦截器,该拦截器控制对安全对象(例如方法调用或 Web 请求)的访问。 AccessDecisionManager
决定是否允许进行调用。
24.2.1 AccessDecisionManager
AccessDecisionManager
由AbstractSecurityInterceptor
调用,并负责做出最终的访问控制决策。 AccessDecisionManager
接口包含三种方法:
void decide(Authentication authentication, Object secureObject,
Collection<ConfigAttribute> attrs) throws AccessDeniedException;
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
传递AccessDecisionManager
的decide
方法所需的所有相关信息,以进行授权决策。特别是,传递安全Object
可以检查实际安全对象调用中包含的那些参数。例如,假设安全对象是MethodInvocation
。在MethodInvocation
中查询任何Customer
参数,然后在AccessDecisionManager
中实现某种安全性逻辑以确保允许委托人对该 Client 进行操作将很容易。如果访问被拒绝,则预期实现会抛出AccessDeniedException
。
AbstractSecurityInterceptor
在启动时会调用supports(ConfigAttribute)
方法,以确定AccessDecisionManager
是否可以处理传递的ConfigAttribute
。安全拦截器实现调用supports(Class)
方法,以确保已配置的AccessDecisionManager
支持安全拦截器将呈现的安全对象的类型。
24.2.2 基于投票的 AccessDecisionManager 实现
尽管用户可以实现自己的AccessDecisionManager
来控制授权的各个方面,但 Spring Security 包括几种基于投票的AccessDecisionManager
实现。 图 24.1,“投票决策 Management 器”说明了相关的类。
图 24.1. 投票决策 Manager
使用此方法,将根据授权决策轮询一系列AccessDecisionVoter
实现。然后AccessDecisionManager
根据其对投票的评估来决定是否抛出AccessDeniedException
。
AccessDecisionVoter
界面具有三种方法:
int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs);
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
具体的实现返回int
,可能的值反映在AccessDecisionVoter
静态字段ACCESS_ABSTAIN
,ACCESS_DENIED
和ACCESS_GRANTED
中。如果投票实施对授权决定没有意见,则将返回ACCESS_ABSTAIN
。如果确实有意见,则必须返回ACCESS_DENIED
或ACCESS_GRANTED
。
Spring Security 提供了三个具体的AccessDecisionManager
来对选票进行汇总。 ConsensusBased
实现将基于未弃权票的共识来授予或拒绝访问权限。提供属性以控制在票数相等或所有票都弃权的情况下的行为。如果收到一个或多个ACCESS_GRANTED
投票,则AffirmativeBased
实现将授予访问权限(即,如果有至少一个授予投票,则拒绝投票将被忽略)。与ConsensusBased
实现类似,有一个参数可以控制所有投票者弃权时的行为。 UnanimousBased
提供者希望获得一致的ACCESS_GRANTED
投票,才能授予访问权限,而忽略弃权。如果有ACCESS_DENIED
票,它将拒绝访问。与其他实现一样,如果所有投票者都弃权,则有一个控制行为的参数。
可以实现自定义AccessDecisionManager
,以不同的方式计算票数。例如,来自特定AccessDecisionVoter
的选票可能会获得额外的权重,而来自特定选民的拒绝选票可能会产生否决权。
RoleVoter
Spring Security 附带的最常用的AccessDecisionVoter
是简单的RoleVoter
,它会将配置属性视为简单的角色名称,并投票给用户(如果已为该用户分配了该角色)。
如果任何ConfigAttribute
以前缀ROLE_
开头,它将投票。如果有GrantedAuthority
返回(通过getAuthority()
方法通过getAuthority()
精确地等于一个或多个ConfigAttributes
)的GrantedAuthority
表示形式,它将投票授予访问权限。如果没有任何与ROLE_
开头的ConfigAttribute
完全匹配,则RoleVoter
将投票拒绝访问。如果没有ConfigAttribute
以ROLE_
开头,则投票者将弃权。
AuthenticatedVoter
我们暗中看到的另一个投票者是AuthenticatedVoter
,它可以用来区分匿名用户,完全认证用户和记住我认证用户。许多站点允许使用“记住我”身份验证进行某些受限访问,但是要求用户通过登录以进行完全访问来确认其身份。
当我们使用属性IS_AUTHENTICATED_ANONYMOUSLY
授予匿名访问权限时,该属性已由AuthenticatedVoter
处理。有关更多信息,请参见 Javadoc。
Custom Voters
显然,您还可以实现自定义AccessDecisionVoter
,并且可以在其中添加几乎任何所需的访问控制逻辑。它可能特定于您的应用程序(与业务逻辑相关),也可能实现某些安全 Management 逻辑。例如,您将在 Spring 网站上找到一个blog article,其中描述了如何使用投票器实时拒绝帐户被暂停的用户的实时访问。
24.3 调用处理后
虽然在进行安全对象调用之前由AbstractSecurityInterceptor
调用AccessDecisionManager
,但是某些应用程序需要一种方法来修改安全对象调用实际返回的对象。尽管您可以轻松实现自己的 AOP 问题来实现这一点,但 Spring Security 提供了一个方便的钩子,该钩子具有一些与其 ACL 功能集成的具体实现。
图 24.2,“调用后实现”说明了 Spring Security 的AfterInvocationManager
及其具体实现。
图 24.2. 调用实现后
像 Spring Security 的许多其他部分一样,AfterInvocationManager
有一个具体的实现AfterInvocationProviderManager
,它轮询AfterInvocationProvider
的列表。允许每个AfterInvocationProvider
修改返回对象或抛出AccessDeniedException
。实际上,由于前一个提供程序的结果将传递到列表中的下一个,因此多个提供程序可以修改该对象。
请注意,如果您使用的是AfterInvocationManager
,则仍然需要允许MethodSecurityInterceptor
的AccessDecisionManager
进行操作的配置属性。如果您使用的是典型的 Spring Security 包含的AccessDecisionManager
实现,则没有为特定的安全方法调用定义配置属性,将导致每个AccessDecisionVoter
放弃投票。反过来,如果AccessDecisionManager
属性“ allowIfAllAbstainDecisions”为false
,则会抛出AccessDeniedException
。您可以通过(i)将“ allowIfAllAbstainDecisions”设置为true
(尽管通常不建议这样做)或(ii)简单地确保至少有一个配置属性供AccessDecisionVoter
投票以授予访问权限来避免此潜在问题。后一种(推荐)方法通常通过ROLE_USER
或ROLE_AUTHENTICATED
配置属性来实现。
24.4 层次角色
通常要求应用程序中的特定角色应自动“包括”其他角色。例如,在具有“Management 员”和“用户”角色概念的应用程序中,您可能希望 Management 员能够执行普通用户可以执行的所有操作。为此,您可以确保还为所有 Management 员用户分配了“用户”角色。或者,您可以修改每个需要“用户”角色也要包括“Management 员”角色的访问约束。如果您的应用程序中有很多不同的角色,这可能会变得非常复杂。
使用角色层次结构可让您配置哪些角色(或权限)应包括其他角色。 Spring Security 的RoleVoter,RoleHierarchyVoter
的扩展版本配置了RoleHierarchy
,从中可以获取为用户分配的所有“可访问权限”。典型的配置可能如下所示:
<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>
在这里,我们在层次结构ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST
中有四个角色。通过ROLE_ADMIN
进行身份验证的用户的行为类似于针对使用以上RoleHierarchyVoter
配置的AccessDecisionManager
c 评估安全约束时将扮演所有四个角色。可以将>
符号视为“包含”。
角色层次结构为简化应用程序的访问控制配置数据和/或减少需要分配给用户的权限数量提供了一种方便的方法。对于更复杂的要求,您可能希望在应用程序需要的特定访问权限与分配给用户的角色之间定义逻辑 Map,并在加载用户信息时在两者之间进行转换。