Spring Security 自定义(第一部分 - 自定义 UserDetails 或扩展 GrantedAuthority)

工程 | Oleg Zhurakousky | 2009年1月2日 | ...

这是我希望成为一系列小型文章的多部分系列的第一部分,这些文章展示了围绕 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"&gt;Google&lt;/a> </sec:authorize> @Secured("ROLE_ADMIN") public void foo()     . . . }

同一用户应被授予对受相应业务功能保护的任何资源的访问权限。 <sec:authorize ifAllGranted="BF_POLICY_DELETE">     <p><a href="http://www.google.com"&gt;Google&lt;/a> </sec:authorize> @Secured("BF_POLICY_DELETE") public void foo()     . . . }

实际上有几种方法可以处理此需求。其中一种方法是创建一个 RoleHierarchy 并使用 RoleHierarchyVoter 遍历角色层次结构。这种方法的缺点是,在 Spring Security 2.0.4 的当前实现中,标签库(security: authorize …)不会通过AccessDecisionManager 来做出决策,因此在做出关于保护 HTML 元素的决策时,Voter 不会发挥任何作用。但是,鉴于 Spring Security 惊人的灵活性和自定义能力,实现此需求仍然非常简单。Spring Security 最大的优势之一是围绕如何创建Principal(UserDetails 对象)的自定义。创建UserDetails 对象时,会使用GrantedAuthority 列表对其进行填充。稍后会检查此列表以匹配保护资源的GrantedAuthority。我们可以进行的自定义之一是在创建UserDetails 对象期间自定义GrantedAuthority 列表。

在提供的示例中,有两个属性文件(为了简化起见,我使用属性文件,但是您可以轻松地修改它以使用数据库或 LDAP)。一个文件users.properties 将用户映射到角色 oleg=powder,ROLE_ADMIN 而另一个文件role-to-bf.properties 将角色映射到业务功能列表 ROLE_ADMIN=BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE 我们的目标是创建一个UserDetails 对象,其中包含一个GrantedAuthority 列表,该列表表示角色业务功能。例如:对于用户olegGrantedAuthority 列表应为: ROLE_ADMIN,BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE 因此,我们只需要定义 UserDetailsService 的自定义实现,在其中使用这两个属性文件(在现实生活中可能是数据库或 LDAP),我们将创建自定义的GrantedAuthority 列表,然后将它们注入到最终的UserDetails 对象中。这非常简单,我们可以重用GrantedAuthority 接口的现有实现,例如 GrantedAuthorityImpl。但是,我们还想确保我们可以跟踪(用于调试或任何其他目的)每个表示业务功能的 GrantedAuthority 的GrantedAuthority。为了实现这两个目标,我们将通过定义一个BusinessFunctionGrantedAuthority 类来扩展GrantedAuthorityImpl,该类仅包含定义此类业务功能的所有父GrantedAuthority 对象的列表。 public class BusinessFunctionGrantedAuthority extends GrantedAuthorityImpl {     private List<GrantedAuthority> parentAuthorities;         . . . }

然后,我们将创建 UserDetailsService 的自定义实现并实现loadUserByName(..) 方法,在其中我们将执行以下操作

1. 基于 users.properties 文件的内容创建 UserAttribute 对象。UserAttribute 将包含表示角色的 GrantedAuthority 列表。
2. 遍历角色-GrantedAuthority 列表,并为每个角色-GrantedAuthority 创建一个 BusinessFunctionGrantedAuthority 并将其添加到已创建的 GrantedAuthority 的整体列表中
    2.1 将父 GrantedAuthority 添加到每个 BusinessFunctionGrantedAuthority
3. 创建包含完整 GrantedAuthority 列表的最终 UserDetails 对象。

然后在您的 Spring Security 配置中定义您的AuthenticationProvider

注意:我们正在使用 ComplexAuthorityUserDetailsService 类实现的自定义 UserDetailsService 注入 AuthenticationProvider。(有关更多详细信息,请参阅示例代码)
保护您的资源,部署并访问应用程序:https://127.0.0.1:8080/spring-security-sample-grantedAuthority/index.jsp
登录后,您应该会看到显示的GrantedAuthority 列表以及Principal 的其他属性

您可以清楚地看到,表示业务功能GrantedAuthority 也显示了定义此类业务功能的父GrantedAuthority 列表。检查index.jsp 并观察security:authorize 标签如何使用角色业务功能来保护 HTML 元素。就是这样。您可以清楚地看到,通过一些小的自定义,您可以轻松地扩展和自定义 Principal 的结构,并基于自定义 GrantedAuthority 应用声明性保护,而无需使用自定义安全代码污染您的业务代码。

示例代码可以从此处下载:spring-security-sample-grantedauthority

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加快您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部