7.3 控制访问
身份验证只是Spring Security安全保护机制中的第一步。一旦Spring Security弄清用户的身份后,它必须决定是否允许用户访问由它保护的资源。在图7.1中,我们已经配置了认证管理器。现在,该配置访问决策管理器 了。访问决策管理器负责决定用户是否拥有恰当的权限访问受保护的资源。它们是由 org.acegisecurity.AccessDecisionManager接口定义的:
这里的supports()方法根据受保护资源的类类型以及它的配置属性(受保护资源的访问要求)来 判断访问决策管理器是否能够针对该资源做出访问决策。这里的decide()方法是完成最终决定的地方。如果它没有抛出一个 AccessDeniedException或者是InsufficientAuthenticationException而返回,则允许访问受保护的 资源。否则,访问被拒绝。
7.3.1 访问决策投票
Spring Security的访问决策管理器最终负责为一个已通过身份验证的用户确定访问权限。不过,它们并不是完全依靠自己而完成这些决策的,而是通过征询一个或 多个对某一用户是否有权访问受保护资源进行投票的对象。一旦获得所有的投票结果,决策管理器便将统计得票情况,并做出它的最终决定。
如表7.3所列,Spring Security带有AccessDecisionManager的三个实现,每一个都采用一种不同的方法来统计得票情况。
表7.3 Spring Security的访问决策管理器通过统计是否允许
某一用户进入的得票来帮助决定是否同意该用户访问
访问决策管理器 | 如何决定同意 / 拒绝访问 |
org.acegisecurity.vote.AffirmativeBased | 只要有一个投票者投票赞成授予访问权,就允许访问 |
org.acegisecurity.vote.ConsensusBased | 只要大多数投票者投票赞成授予访问权,就允许访问 |
org.acegisecurity.vote.UnanimousBased | 只有当所有投票者都投票赞成授予访问权时,才允许访问 |
在Spring配置文件中,所有的访问决策管理器都是以相同的方式进行配置的。比如说,下面这一段XML选段配置了一个UnanimousBased访问决策管理器:
通过这里的decisionVoters属性,你可以为访问决策管理器提供一组投票者。在上述情况中,只有一个投票者,它引用了一个名为roleVoter的Bean。下面让我们看一下这个roleVoter是如何配置的。
7.3.2 决定如何投票
尽管访问决策投票者对是否授权访问某个受保护的资源没有最终发言权(该项工作属于访问决策管理器),但是它们在访问决策过程中扮演着重要的角色。一 个访问决策投票者的工作是同时考虑用户已拥有的授权和受保护资源配置属性中所要求的授权。基于这些信息,访问决策投票者通过投票为访问决策管理器做出决策 提供支持。
任何实现org.acegisecurity.vote.AccessDecisionVoter接口的对象都是一个访问决策投票者:
可以看到,这个AccessDecisionVoter接口和AccessDecisionManager接口非常相似。最大的区别在于,这里没有 返回void的decide()方法,而是一个返回int的vote()方法。那是因为访问决策投票者并不决定是否允许访问,它仅仅就是否授予访问权限投 出它自己的一票。
在获得投票机会时,访问决策投票者能够以下面三种方式之一进行投票:
ACCESS_GRANTED——投票者希望允许访问受保护的资源。
ACCESS_DENIED——投票者希望拒绝对受保护资源的访问。
ACCESS_ABSTAIN——投票者保持中立。
与绝大多数的Spring Security组件相同,你也可以自由地编写自己的AccessDecision Voter的实现类。然而,Spring Security提供有一个很实用的投票者实现类RoleVoter,它在受保护资源的配置属性代表一个角色时进行投票。说得更具体一些,就是在受保护资 源有一个名称以ROLE_打头的配置属性时,RoleVoter参与投票。
RoleVoter决定其投票结果的方式是通过简单地将受保护资源的所有配置属性(以ROLE_作为前缀)与已授予认证用户的所有权限进行比较。如 果RoleVoter发现其中有一个是匹配的,则它投ACCESS_GRANTED票。否则,它将投ACCESS_DENIED票。
RoleVoter将只在访问所需的授权不是以ROLE_为前缀时放弃投票。举例来说,如果受保护的资源仅仅要求非角色的授权(比如说CREATE_USER),那么RoleVoter将放弃投票。
你可以在Spring配置文件中使用下列XML配置一个RoleVoter:
如前所述,RoleVoter只有在受保护资源具有以ROLE_为前缀的配置属性时才进行投票。然而,这个ROLE_前缀只是默认值。你可以选择通过设置rolePrefix属性来覆盖这个默认前缀:
在这里,默认的前缀已被覆盖为GROUP_。因此,这个RoleVoter现在将只针对以GROUP_为前缀的权限进行授权投票。
7.3.3 处理投票弃权
在知道了任何一个投票者都可以投允许、拒绝或弃权票之后,现在你可能会问:如果所有投票者都投弃权票,会发生什么呢?那个用户究竟会被授权还是拒绝访问?
在默认情况下,如果所有投票者全都投弃权票,那么所有的访问决策管理者都将拒绝对资源的访问。不过,你可以通过将访问决策管理者的allowIfAllAbstain属性设置为true来覆盖这个默认行为:
通过把allowIfAllAbstain设置为true,你就确立了一个“沉默即同意”的政策。换句话说,如果所有的投票者都放弃投票,则如同它们都投赞成票一样,访问将被授权。
现在,你已经看到Spring Security的身份验证和访问控制管理器是如何工作的了,下面让我们把它们投入使用。在下一节中,你将看到如何使用Spring Security的一组Servlet过滤器来保护一个Web应用程序。稍后,在第7.6节,我们将深入到一个应用程序内部,考察如何使用Spring AOP在方法调用级上实施保护。