领先一步
VMware 提供培训和认证,助您加速进步。
了解更多这是我希望成为一系列关于 Spring Security 定制的小帖子系列的第一部分,展示了实际示例。这些定制的需求并非虚构,全部来自于实际应用中。。。
假设您有以下需求。您有一个角色列表,其中每个角色都包含适用于该角色的业务功能列表(见下文)
ROLE_ADMIN BF_QUOTE_CREATE BF_POLICY_CREATE BF_POLICY_DELETE
ROLE_AGENT BF_QUOTE_CREATE BF_POLICY_CREATE
ROLE_USER BF_QUOTE_CREATE
关键在于能够基于两者做出授权决策。
例如: 拥有 ROLE_ADMIN 角色的用户应被授权访问受此角色保护的任何资源。 <sec:authorize ifAllGranted="ROLE_ADMIN"> <p><a href="http://www.google.com">Google</a> </sec:authorize>
或 @Secured("ROLE_ADMIN") public void foo() . . . }
同一用户应被授权访问受相应业务功能保护的任何资源。 <sec:authorize ifAllGranted="BF_POLICY_DELETE"> <p><a href="http://www.google.com">Google</a> </sec:authorize>
或 @Secured("BF_POLICY_DELETE") public void foo() . . . }
实际上有几种方法可以处理此需求。其中一种方法是创建一个 RoleHierarchy 并使用 RoleHierarchyVoter 来遍历角色层次结构。这种方法的缺点是,在当前 Spring Security 2.0.4 的实现中,标签库 (security: authorize . . . ) 不会通过 AccessDecisionManager 来做出决策,因此在决定保护 HTML 元素时,Voters 不起任何作用。然而,考虑到 Spring Security 惊人的灵活性和定制能力,完成此需求仍然相当简单。Spring Security 最大的优势之一是可以围绕如何创建 Principal (UserDetails 对象) 进行定制。创建 UserDetails 对象时,会填充 GrantedAuthorities 列表。之后会检查此列表,以匹配保护资源的 GrantedAuthority。我们可以进行的一项定制是在创建 UserDetails 对象期间定制 GrantedAuthorities 列表。
在提供的示例中,有两个属性文件(为简化起见,我使用属性文件,但您可以轻松修改为使用 DB 或 LDAP)。一个文件 users.properties 将用户映射到 roles oleg=powder,ROLE_ADMIN
,而另一个文件 role-to-bf.properties 将角色映射到 business functions 的列表 ROLE_ADMIN=BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE
。我们的目标是创建一个 UserDetails 对象,其中包含表示 roles 和 business functions 的 GrantedAuthorities 列表。例如:对于用户 oleg,GrantedAuthorities 列表应为: ROLE_ADMIN,BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE
。因此,我们只需定义一个 UserDetailsService 的自定义实现,通过使用这两个属性文件(在实际应用中可以是 DB 或 LDAP),我们将创建自定义的 GrantedAuthorities 列表,然后将它们注入到最终的 UserDetails 对象中。这相当简单,我们可以重用现有的 GrantedAuthority 接口实现,例如 GrantedAuthorityImpl。然而,我们还想确保能够追溯(用于调试或任何其他目的)表示业务功能的每个 GrantedAuthority 的父级 GrantedAuthority。为了同时实现这两个目标,我们将通过定义一个 BusinessFunctionGrantedAuthority 类来扩展 GrantedAuthorityImpl,该类简单地包含定义此类 business function 的所有父级 GrantedAuthority 对象的列表。 public class BusinessFunctionGrantedAuthority extends GrantedAuthorityImpl { private List<GrantedAuthority> parentAuthorities; . . . }
然后,我们将创建一个 UserDetailsService 的自定义实现,并实现 loadUserByName(..) 方法,在该方法中我们将执行以下操作
1. 根据 users.properties 文件的内容创建 UserAttribute 对象。UserAttribute 将包含表示角色的 GrantedAuthorities 列表。
2. 遍历 role-GratedAuthorities 列表,并为每个 role-GrantedAuthority 创建一个 BusinessFunctionGrantedAuthority,并将其添加到已创建的 GrantedAuthorities 总列表。
2.1 将父 GrantedAuthority 添加到每个 BusinessFunctionGrantedAuthority。
3. 创建包含 GrantedAuthorities 完整列表的最终 UserDetails 对象。
然后在您的 Spring Security 配置中定义您的 AuthenticationProvider
注意:我们正在将 AuthenticationProvider 注入到由 ComplexAuthorityUserDetailsService 类实现的自定义 UserDetailsService 中(更多详细信息请参阅示例代码)。
保护您的资源,部署并访问应用程序:http://localhost:8080/spring-security-sample-grantedAuthority/index.jsp
登录后,您应该会看到显示的 GrantedAuthorities 列表以及 Principal 的其他属性。
您可以清楚地看到,表示 business function 的 GrantedAuthority 也显示了定义此类 business function 的父级 GratedAuthorities 列表。检查 index.jsp 并观察 security:authorize 标签如何同时使用 roles 和 business functions 来保护 HTML 元素。这就是全部了。您可以清楚地看到,通过少量定制,您可以轻松扩展和定制 Principal 的结构,并基于自定义 GrantedAuthorities 应用声明式保护,而无需在业务代码中混入自定义安全代码。
示例代码可在此下载:spring-security-sample-grantedauthority