将现有的域类与 Spring Security 插件一起使用
Posted
技术标签:
【中文标题】将现有的域类与 Spring Security 插件一起使用【英文标题】:use existing domain classes with Spring Security plugin 【发布时间】:2011-10-17 11:49:54 【问题描述】:我正在尝试将 Stripes 网络应用程序转换为 Grails。 Stripes 应用程序使用 Spring Security,但我希望 Grails 应用程序使用 Spring Security Grails 插件。
应用程序已经有 User
和 Role
(Java) 类,我需要重用它们,即我无法使用 s2-quickstart 脚本生成的 Grails 域类。
Spring Security 插件文档描述了如何使用现有的User
域类。步骤似乎是:
-
定义一个从现有
User
域类中读取的UserDetails
实现
定义返回 (1) 实例的自定义 UserDetailsService
实现
将 (2) 的实例注册为名为 userDetailsService
的 Spring bean。
但是,文档没有提供有关如何使用现有 Role
类以及表示 User
和 Role
之间多对多关系的类的任何信息。
将现有的Role
、User
和UserRole
类与 Grails Spring Security 插件一起使用还需要哪些其他步骤?如果我不想生成任何域类,我有什么理由运行 s2-quickstart 脚本吗?
Burt 回答的后续问题
最后,你需要的是一个新的
GrailsUser
大概GrailsUser
这里指的是自定义的UserDetails
实现吧?就我而言,我可能会直接实现接口。这样的事情看起来合理吗?
class UserAdapter implements UserDetails
private String password
private Collection<GrantedAuthority> springRoles
UserAdapter(User user)
this.password = user.password
Collection<Role> roles = // load legacy Role objects
this.springRoles = roles.collect new GrantedAuthorityImpl(it.authority)
// If using password hashing, presumably this is the hashed password?
String getPassword()
password
///////// other UserDetails methods omitted
Collection<GrantedAuthority> getAuthorities()
springRoles
我没有将整个 User
对象存储在 UserAdapter
中,因为您警告说在 HTTP 会话中存储可能很大的对象。
你需要的是.....和一个 GrantedAuthority 实例的列表(如果它是 GrailsUser,还有 id)
如果我如上所述使用我自己的UserDetails
实现,那么大概我可以忽略这条关于提供id
的评论?
最后,如果我遵循上述方法,我是否应该在Config.groovy
中设置these properties 并且是否需要运行s2-quickstart
脚本(或任何其他脚本)?
【问题讨论】:
看起来不错。您可以在 UserDetailsService 中计算角色 -> GrantedAuthority,然后使用 Spring Security User 或插件的 GrailsUser 作为 POJO,但这也很好。是的,这是来自数据库的哈希密码。明文密码将被加密并与之进行比较。角色的东西看起来不错,显然你需要布尔吸气剂。我将 ID 保留在 UserDetails 中,以便轻松重新加载源用户,但如果您不需要它,请忽略它。您也可以使用用户名,它很可能具有唯一索引,并且几乎与 PK 查询一样高效。 如果您使用的是自定义 UserDetailsService,则不会使用 Config.groovy 中的类名,因为这仅用于插件的 UserDetailsService。 s2-quickstart 将为您提供登录/注销控制器和 GSP,因此您可以使用虚拟包运行它并删除生成的域类。 再次感谢,非常有帮助。最后一个问题,您说“我将 id 保留在 UserDetails 中,以便轻松重新加载源用户”。您如何知道何时在 UserDetails 中重新加载用户?我假设您不会在每次调用方法时都这样做? 它不是来自 UserDetails,它通常在初始登录后的控制器请求中。 UserDetails 很容易访问,因为它是身份验证“主体”,所以如果 id 在那里,您可以通过“User.get(principal.id)”获取用户。您也可以执行“User.findByUsername(principal.username)”,但正如我所说,只有在用户名上有索引时才有效。 知道了,在我的情况下,用户名有一个唯一索引,所以我不需要将id
添加到用户详细信息中。我认为使用 id
而不是 username
可能会有一些缓存优势,但目前我更感兴趣的是让事情正常工作而不是优化性能。
【参考方案1】:
在 config.groovy 文件中,您必须指定要使用的域类:
grails.plugins.springsecurity.userLookup.userDomainClassName = 'your.package.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'your.package.UserRole'
grails.plugins.springsecurity.authority.className = 'your.package.Role'
我认为没有必要实现自己的 userDetail 服务,因为 spring security 使用
SpringSecurityUtils.securityConfig.userLookup
确定您之前配置的域类的方法。您的域类必须提供必需的字段和关系。
【讨论】:
如果您的用户域类已经实现了UserDetails
,您可能不需要提供自己的UserDetailsService
,但在我的情况下不需要【参考方案2】:
请记住,Spring Security 并不关心数据来自哪里,它只需要一个 UserDetails
实例,在使用 DAO 身份验证提供程序进行身份验证时,它可以来自任何地方。使用域类和数据库表很方便,但这只是一种方法。做对您的数据有用的事情。最后,您需要的是一个新的 GrailsUser
(或其他一些 impl)实例,其中包含 username
和 password
集、3 个布尔集和 GrantedAuthority
实例列表(以及 id
如果是GrailsUser
)。
当您拥有旧用户和角色数据时,最简单的做法是创建自定义 UserDetailsService
。使用 GORM、原始 SQL 查询,无论您需要什么来获取所需的数据。
另一种选择是编写自己的AuthenticationProvider
,就像 Glen 在此处所做的那样:http://blogs.bytecode.com.au/glen/2010/01/15/hacking-custom-authentication-providers-with-grails-spring-security.html - 尽管这是一个更大的解决方案,还涉及您不需要的自定义过滤器。 DAO 提供程序使用UserDetailsService
,但可以创建自己的将功能组合到一个类中。
尽管重用您的User
域类作为UserDetails
并不是一个好主意。即使您实现了该接口,您也会在 HTTP 会话中存储一个断开连接的潜在大(如果有附加的集合)对象。 POJO/POGO 实现(Spring Security 的User
类、插件的GrailsUser
类等)非常小,只有几个字符串和布尔值。
【讨论】:
非常有帮助,非常感谢。我在上面发布了一些后续问题 我听从了你的建议,效果很好——非常感谢以上是关于将现有的域类与 Spring Security 插件一起使用的主要内容,如果未能解决你的问题,请参考以下文章
grails 和 spring security acl:仅显示域类的一些实例
grails spring security ui在用户域类上没有电子邮件字段
将现有的 Spring 应用程序转换为 Spring-Boot