# 授权体系结构
# 当局
[Authentication
](../authentication/architecture.html# Servlet-authentication-authentication),讨论了所有Authentication
实现如何存储GrantedAuthority
对象的列表。这些代表已授予校长的权力。GrantedAuthority
对象由AuthenticationManager
插入到Authentication
对象中,然后在做出授权决策时由AuthorizationManager
读取。
GrantedAuthority
是一个只有一个方法的接口:
String getAuthority();
这种方法允许AuthorizationManager
s 得到精确的String
表示的GrantedAuthority
。通过以String
的形式返回表示,大多数AuthorizationManager
s 和AccessDecisionManager
s 都可以轻松地“读取”GrantedAuthority
s。如果GrantedAuthority
不能精确地表示为String
,则GrantedAuthority
被认为是“复杂的”,而getAuthority()
必须返回null
。
“complex”GrantedAuthority
的一个示例是一个实现,该实现存储了适用于不同客户帐号的操作和权限阈值的列表。将这个复杂的GrantedAuthority
表示为String
将非常困难,因此getAuthority()
方法应该返回null
。这将向任何AuthorizationManager
表示,为了理解其内容,它将需要特别支持GrantedAuthority
实现。
Spring 安全性包括一个具体的GrantedAuthority
实现,SimpleGrantedAuthority
。这允许将任何用户指定的String
转换为GrantedAuthority
。安全体系结构中包含的所有AuthenticationProvider
都使用SimpleGrantedAuthority
来填充Authentication
对象。
# 调用前处理
Spring 安全性提供了拦截器,该拦截器控制对安全对象的访问,例如方法调用或 Web 请求。关于是否允许调用继续进行的调用前决定由AccessDecisionManager
做出。
# 授权经理
AuthorizationManager
同时取代[AccessDecisionManager
和AccessDecisionVoter
](#authz-legacy-note)。
鼓励定制AccessDecisionManager
或AccessDecisionVoter
的应用程序[更改为使用AuthorizationManager
](#authz-voter-adaption)。
AuthorizationManager
s 由[AuthorizationFilter
]调用,并负责做出最终的访问控制决策。AuthorizationManager
接口包含两种方法:
AuthorizationDecision check(Supplier<Authentication> authentication, Object secureObject);
default AuthorizationDecision verify(Supplier<Authentication> authentication, Object secureObject)
throws AccessDeniedException {
// ...
}
将传递AuthorizationManager
的check
方法所需的所有相关信息,以便做出授权决定。特别地,传递 SecureObject
使实际安全对象调用中包含的那些参数能够被检查。例如,假设安全对象是MethodInvocation
。对于任何Customer
参数,都可以很容易地查询MethodInvocation
,然后在AuthorizationManager
中实现某种安全逻辑,以确保主体被允许对该客户进行操作。如果授予访问,则预期实现将返回正的,如果拒绝访问,则返回负的,并且在不做出决定时返回空的。
verify
调用check
,然后在负数AuthorizationDecision
的情况下抛出AccessDeniedException
。
# 基于委托的授权管理器实现
虽然用户可以实现他们自己的AuthorizationManager
来控制授权的所有方面, Spring 安全性提供了一个委托AuthorizationManager
,它可以与单个AuthorizationManager
s 协作。
RequestMatcherDelegatingAuthorizationManager
将请求与最合适的委托匹配AuthorizationManager
。对于方法安全性,可以使用AuthorizationManagerBeforeMethodInterceptor
和AuthorizationManagerAfterMethodInterceptor
。
授权管理器实现说明了相关的类。
图 1。授权管理器实现
使用这种方法,可以在授权决策上对AuthorizationManager
实现的组合进行轮询。
# AuthorityAuthorizationManager
Spring 安全性提供的最常见的AuthorizationManager
是AuthorityAuthorizationManager
。它配置了一组给定的权限,以便在当前Authentication
上查找。如果Authentication
包含任何配置的权限,它将返回正的AuthorizationDecision
。否则它将返回负值AuthorizationDecision
。
# AuthenticatedauthorizationManager
另一个管理器是AuthenticatedAuthorizationManager
。它可以用来区分匿名的,完全认证的和记住我认证的用户。许多网站在 Remember-Me 身份验证下允许某些有限的访问,但需要用户通过登录来确认其身份,以获得完全访问权限。
# 自定义授权管理器
显然,你还可以实现一个自定义AuthorizationManager
,并且你可以在其中放入你想要的任何访问控制逻辑。它可能是特定于你的应用程序的(与业务逻辑相关的),或者它可能实现一些安全管理逻辑。例如,你可以创建一个实现,该实现可以查询 Open Policy Agent 或你自己的授权数据库。
你将在 Spring 网站上找到博客文章 (opens new window),其中描述了如何使用遗留的AccessDecisionVoter 来实时拒绝帐户已被暂停的用户的访问。相反,你可以通过实现 AuthorizationManager 来实现相同的结果。 |
---|
# 适应 accessisodecisionmanager 和 accessidecisionvoters
在AuthorizationManager
之前, Spring 发布了安全性[AccessDecisionManager
和AccessDecisionVoter
](#authz-legacy-note)。
在某些情况下,例如迁移较旧的应用程序,可能需要引入一个调用AuthorizationManager
或AccessDecisionVoter
的AuthorizationManager
。
要调用现有的AccessDecisionManager
,可以执行以下操作:
例 1。适应 accessisdecisionManager
爪哇
@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<ConfigAttributes> 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<ConfigAttributes> attributes = this.securityMetadataSource.getAttributes(object);
this.accessDecisionManager.decide(authentication.get(), object, attributes);
}
}
然后把它连接到你的SecurityFilterChain
。
或者只调用AccessDecisionVoter
,你可以这样做:
例 2。适应辅助决策投票人
爪哇
@Component
public class AccessDecisionVoterAuthorizationManagerAdapter implements AuthorizationManager {
private final AccessDecisionVoter accessDecisionVoter;
private final SecurityMetadataSource securityMetadataSource;
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, Object object) {
Collection<ConfigAttributes> 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 Security 的投票人
,RoleHierarchyVoter
的扩展版本配置了RoleHierarchy
,它从该版本获得分配给用户的所有“可访问权限”。一个典型的配置可能是这样的:
例 3。分层角色配置
爪哇
@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 RoleHierarcyVoter(hierarchy);
}
XML
<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
的AuthorizationManager
的安全约束进行评估时,将表现为他们拥有所有四个角色。>
符号可以被认为是“包括”的意思。
角色层次结构为简化应用程序的访问控制配置数据和/或减少需要分配给用户的权限数量提供了一种方便的方法。对于更复杂的需求,你可能希望在应用程序所需的特定访问权限和分配给用户的角色之间定义一个逻辑映射,在加载用户信息时在这两者之间转换。
# 遗留授权组件
Spring 安全性包含一些遗留组件。 由于它们尚未被删除,因此出于历史目的而包含文档。 它们的建议替换在上面。 |
---|
# 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
方法所需的所有相关信息,以便做出授权决定。特别地,传递 Secure使那些包含在实际安全对象调用中的参数能够被检查。例如,假设安全对象是MethodInvocation
。对于任何Customer
参数,都可以很容易地查询MethodInvocation
,然后在AccessDecisionManager
中实现某种安全逻辑,以确保主体被允许对该客户进行操作。如果访问被拒绝,则期望实现抛出AccessDeniedException
。
supports(ConfigAttribute)
方法在启动时由AbstractSecurityInterceptor
调用,以确定AccessDecisionManager
是否可以处理传递的ConfigAttribute
。安全拦截器实现调用supports(Class)
方法,以确保配置的AccessDecisionManager
支持安全拦截器将呈现的安全对象类型。
# 基于投票的 AccessDecisionManager 实现
虽然用户可以实现他们自己的AccessDecisionManager
以控制授权的所有方面, Spring 安全性包括几个基于投票的AccessDecisionManager
实现。投票决策经理举例说明了相关的类。
图 2。投票决策经理
使用这种方法,对一系列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
。
有三个具体的AccessDecisionManager
s 提供了 Spring 安全,以统计选票。该ConsensusBased
实现将基于非弃权投票的共识授予或拒绝访问。在票数相等或所有投票都弃权的情况下,提供属性以控制行为。如果收到一个或多个ACCESS_GRANTED
投票,AffirmativeBased
实现将授予访问权限(即,如果至少有一个授予投票,则拒绝投票将被忽略)。与ConsensusBased
实现类似,如果所有投票者弃权,则有一个参数来控制行为。UnanimousBased
提供者期望获得一致的ACCESS_GRANTED
票,以授予访问权限,而忽略弃权。如果有任何ACCESS_DENIED
投票,它将拒绝访问。与其他实现一样,如果所有投票者都弃权,则有一个控制行为的参数。
可以实现一个自定义AccessDecisionManager
,该自定义可以以不同的方式对选票进行统计。例如,来自特定AccessDecisionVoter
的投票可能会获得额外的权重,而来自特定选民的拒绝投票可能具有否决效果。
# RoleVoter
Spring 安全性提供的最常用的AccessDecisionVoter
是简单的RoleVoter
,它将配置属性视为简单的角色名称和投票,以在用户已被分配该角色时授予访问权限。
如果有ConfigAttribute
开头的前缀ROLE_
,则将进行投票。如果有一个GrantedAuthority
返回一个String
表示(通过getAuthority()
方法)完全等于一个或多个ConfigAttributes
,它将投票授予访问权限,该表示从前缀ROLE_
开始。如果没有任何以ROLE_
开头的ConfigAttribute
完全匹配,则RoleVoter
将投票拒绝访问。如果不ConfigAttribute
以ROLE_
开头,则投票人将弃权。
# 已验证的投票人
我们隐式看到的另一个投票者是AuthenticatedVoter
,它可以用来区分匿名的、完全验证的和通过 rememe 验证的用户。许多网站在 Remember-Me 身份验证下允许某些有限的访问,但需要用户通过登录来确认其身份,以获得完全访问权限。
当我们使用属性IS_AUTHENTICATED_ANONYMOUSLY
授予匿名访问权限时,这个属性正在被AuthenticatedVoter
处理。有关此类的更多信息,请参见 Javadoc。
# 习俗投票人
显然,你还可以实现一个自定义AccessDecisionVoter
,并且你可以在其中放入你想要的任何访问控制逻辑。它可能是特定于你的应用程序的(与业务逻辑相关的),或者它可能实现一些安全管理逻辑。例如,你将在 Spring 网站上找到一个博客文章 (opens new window),该网站描述了如何使用投票器实时拒绝帐户已被暂停的用户的访问。
图 3。调用实现之后
与 Spring 安全性的许多其他部分一样,AfterInvocationManager
有一个具体的实现,AfterInvocationProviderManager
,它轮询AfterInvocationProvider
s 的列表。每个AfterInvocationProvider
都被允许修改返回对象或抛出AccessDeniedException
。实际上,多个提供者可以修改该对象,因为前一个提供者的结果被传递到列表中的下一个提供者。
请注意,如果你使用AfterInvocationManager
,你仍然需要允许MethodSecurityInterceptor
的AccessDecisionManager
的配置属性来允许操作。如果你使用的是包括AccessDecisionManager
实现的典型 Spring 安全性,那么没有为特定的安全方法调用定义配置属性将导致每个AccessDecisionVoter
放弃投票。反过来,如果AccessDecisionManager
属性“allowifallabstaindecisions”是false
,则将抛出一个AccessDeniedException
。你可以通过(i)将“AllowifallabstainDecisions”设置为true
(尽管通常不建议这样做)或简单地确保至少有一个配置属性(AccessDecisionVoter
将投票授予访问权限)来避免这个潜在的问题。后一种(推荐的)方法通常通过ROLE_USER
或ROLE_AUTHENTICATED
配置属性来实现。