24. Authorization Architecture(授权架构)
24.1 Authorities
正如我们在技术概述中看到的,所有身份验证实现都存储了一个授权对象列表。这些代表委托人被授予的权力。授权权限对象(GrantedAuthority )由身份验证管理器(AuthenticationManager )插入到身份验证对象(Authentication )中,稍后由访问决策管理器(AccessDecisionManager )在做出授权决策时读取。
GrantedAuthority是一个只有一种方法的接口:
此方法允许AccessDecisionManager 获得GrantedAuthority的精确字符串表示。通过以字符串形式返回表示,大多数AccessDecisionManager 可以轻松地“读取”GrantedAuthority 。如果GrantedAuthority 不能精确地表示为字符串,则GrantedAuthority 被认为是“复杂的”,并且getAuthority()必须返回null。
授予“复杂”授权的一个示例是存储适用于不同客户帐号的操作和授权阈值列表的实现。将这个复杂的GrantedAuthority表示为字符串将非常困难,因此getAuthority()方法应该返回null。这将向任何AccessDecisionManager 表明,为了理解其内容,它将需要专门支持GrantedAuthority 的实现。
Spring Security包括一个具体的GrantedAuthority ,SimpleGrantedAuthority。这允许将任何用户指定的字符串转换为GrantedAuthority。安全体系结构中包含的所有AuthenticationProvider 都使用SimpleGrantedAuthority 来填充Authentication 对象。
24.2 Pre-Invocation Handling(调用前处理)
正如我们在“技术概述”一章中看到的,Spring Security提供了拦截器来控制对安全对象的访问,例如方法调用或web请求。是否允许进行调用的调用前决定由访问决策管理器做出。
24.2.1 The AccessDecisionManager( 访问决策管理器)
访问决策管理器(AccessDecisionManager)由抽象安全接口(AbstractSecurityInterceptor )调用,负责做出最终的访问控制决策。访问决策管理器(AccessDecisionManager )界面包含三种方法:
访问决策管理器(AccessDecisionManager's )的decide 方法被传递它需要的所有相关信息,以便做出授权决策。特别是,传递安全对象能够检查包含在实际安全对象调用中的那些参数。例如,让我们假设安全对象是一个方法位置。很容易向方法位置查询任何客户参数,然后在访问决策管理器AccessDecisionManager's 中实现某种安全逻辑,以确保主体被允许让该客户进行操作。如果访问被拒绝,实现应该抛出一个访问拒绝异常AccessDeniedException 。
抽象安全接口(AbstractSecurityInterceptor )在启动时调用支持(配置属性)方法,以确定访问决策管理器(AccessDecisionManager )是否可以处理传递的配置属性(ConfigAttribute)。安全拦截器实现调用支持(类)方法,以确保配置的访问决策管理器(AccessDecisionManager )支持安全拦截器将呈现的安全对象类型。
24.2.2 Voting-Based AccessDecisionManager Implementations
基于投票的访问决策管理器实施
虽然用户可以实现他们自己的访问决策管理器来控制授权的所有方面,但是Spring Security包括几个基于投票的访问决策管理器实现。图24.1,“投票决策管理器”说明了相关的类。
使用这种方法,一系列的访问决策投票器(AccessDecisionVoter )实现在授权决策时被轮询。接入决策管理器(AccessDecisionManager )然后根据其对投票的评估决定是否抛出访问拒绝异常(AccessDeniedException )。
访问决策投票者(AccessDecisionVoter )界面有三种方法:
具体的实现返回一个整数,可能的值反映在访问决定投票者静态字段中ACCESS_ABSTAIN、ACCESS_DENIED、ACCESS_GRANTED。如果投票实现对授权决定没有意见,它将返回ACCESS _ BATTER。如果它确实有意见,它必须返回ACCESS _ DECLARED或ACCESS_GRANTED。
spring安全提供了三个具体的AccessDecisionManager 来统计投票。基于共识的实施将基于非弃权投票的共识授予或拒绝访问权。提供属性是为了在票数相等或所有票弃权的情况下控制行为。如果收到一个或多个ACCESS_GRANTED投票(即拒绝投票将被忽略,前提是至少有一个授予投票),则基于确认的实现将授予访问权限。像基于共识的实现一样,有一个参数可以控制所有投票者弃权时的行为。基于UnanimousBased的提供程序需要一致的ACCESS_GRANTED投票才能授予访问权限,忽略弃权。如果有任何ACCESS _ DECLARED投票,它将拒绝访问。像其他实现一样,如果所有投票者弃权,有一个参数控制行为。
可以实现一个定制的访问决策管理器,以不同的方式统计投票。例如,来自某个特定投票人的投票可能会获得额外的权重,而来自某个特定投票人的拒绝投票可能会产生否决效果。
RoleVoter
Spring Security提供的最常用的访问决策投票器(AccessDecisionVoter )是简单的角色投票器,它将配置属性视为简单的角色名称,并在用户被分配了该角色时投票授予访问权限。
如果任何配置属性以前缀ROLE_开头,它将进行投票。如果有一个授予的权限返回一个字符串表示(通过getAuthority()方法),该字符串表示完全等于一个或多个以前缀ROLE_开头的配置属性,它将投票授予访问权限。如果没有以ROLE_开头的任何配置属性的完全匹配,ROLE选民将投票拒绝访问。如果没有以ROLE_开头的配置属性,投票人将弃权。
AuthenticatedVoter
我们已经隐式看到的另一个投票者是授权投票者,它可以用来区分匿名的、完全授权的和记住我的授权用户。
许多网站允许在“记住我”身份验证下进行某些有限的访问,但要求用户通过登录进行完全访问来确认其身份。当我们使用属性IS _ AUTHENTICATED _ ANONYMOUSLY来授予匿名访问时,该属性正由AUTHENTICATED选民处理
Custom Voters
显然,您还可以实现一个自定义的访问决策投票器,并且您可以在其中放入任何您想要的访问控制逻辑。它可能特定于您的应用程序(与业务逻辑相关),也可能实现一些安全管理逻辑。例如,您可以在Spring网站上找到一篇博客文章,文章描述了如何使用投票者实时拒绝帐户被暂停的用户访问。
24.3 After Invocation Handling(调用后处理)
在继续安全对象调用之前,抽象安全接口调用访问决策管理器,而一些应用程序需要一种方法来修改安全对象调用实际返回的对象。虽然您可以很容易地实现您自己的AOP关注点来实现这一点,但是Spring Security提供了一个方便的钩子,它有几个与它的ACL功能集成的具体实现。
图24.2,“调用后实现”展示了Spring Security的“调用后管理器”及其具体实现。
像Spring Security的许多其他部分一样,后调用管理器(AfterInvocationManager )有一个具体的实现,即后调用提供者管理器(AfterInvocationProviderManager),它轮询后调用提供者的列表。允许每个AfterInvocationProvider修改返回对象或抛出一个AccessDeniedException。事实上,多个提供者可以修改对象,因为前一个提供者的结果会传递给列表中的下一个提供者。
请注意,如果您使用的是后调用管理器(AfterInvocationManager),您仍然需要配置属性来允许MethodSecurityInterceptor的访问决策管理器(AccessDecisionManager )允许操作。如果您使用的是典型的Spring Security包含的访问决策管理器(AccessDecisionManager )实现,没有为特定的安全方法调用定义配置属性将导致每个访问决策投票者(AccessDecisionVoter )放弃投票。反过来,如果访问决策管理器属性“allowIfAllAbstainDecisions”为假,将引发访问拒绝异常(AccessDeniedException )。您可以通过以下两种方式避免此潜在问题:(1)将“allowIfAllAbstainDecisions”设置为true(尽管通常不建议这样做),或者(2)简单地确保至少有一个配置属性可供访问决策投票人投票授予访问权限。后一种(推荐的)方法通常是通过角色用户(ROLE_USER )或角色认证(ROLE_AUTHENTICATED )配置属性实现的。
24.4 Hierarchical Roles(分层角色)
通常要求应用程序中的特定角色应该自动“包括”其他角色。例如,在具有“管理员”和“用户”角色概念的应用程序中,您可能希望管理员能够做普通用户能做的一切事情。要实现这一点,您可以确保所有管理员用户也被分配了“用户”角色。或者,您可以修改要求“用户”角色也包括“管理员”角色的每个访问约束。如果您在应用程序中有许多不同的角色,这可能会变得非常复杂。
角色层次结构的使用允许您配置哪些角色(或权限)应该包含其他角色。Spring Security角色投票器的一个扩展版本,角色层次投票器(RoleHierarchyVoter),配置了一个角色层次(RoleHierarchy),它从角色层次中获取用户被分配的所有“可达权限”。典型的配置可能如下所示:
在这里,我们在一个层级中有四个角色ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST。使用ROLE_ADMIN进行身份验证的用户,在根据使用上面的RoleHierarchyVoter配置的访问决策管理器评估安全约束时,将表现得好像他们拥有所有四个角色。符号>可以被认为是“包含”的意思。
角色层次结构为简化应用程序的访问控制配置数据和/或减少需要分配给用户的权限数量提供了一种方便的方法。对于更复杂的需求,您可能希望在应用程序所需的特定访问权限和分配给用户的角色之间定义一个逻辑映射,在加载用户信息时在两者之间进行转换。