将现有的域类与 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 插件。

应用程序已经有 UserRole (Java) 类,我需要重用它们,即我无法使用 s2-quickstart 脚本生成的 Grails 域类。

Spring Security 插件文档描述了如何使用现有的User 域类。步骤似乎是:

    定义一个从现有User 域类中读取的UserDetails 实现 定义返回 (1) 实例的自定义 UserDetailsService 实现 将 (2) 的实例注册为名为 userDetailsService 的 Spring bean。

但是,文档没有提供有关如何使用现有 Role 类以及表示 UserRole 之间多对多关系的类的任何信息。

将现有的RoleUserUserRole 类与 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 脚本(或任何其他脚本)?

【问题讨论】:

看起来不错。您可以在 UserDetailsS​​ervice 中计算角色 -> GrantedAuthority,然后使用 Spring Security User 或插件的 GrailsUser 作为 POJO,但这也很好。是的,这是来自数据库的哈希密码。明文密码将被加密并与之进行比较。角色的东西看起来不错,显然你需要布尔吸气剂。我将 ID 保留在 UserDetails 中,以便轻松重新加载源用户,但如果您不需要它,请忽略它。您也可以使用用户名,它很可能具有唯一索引,并且几乎与 PK 查询一样高效。 如果您使用的是自定义 UserDetailsS​​ervice,则不会使用 Config.groovy 中的类名,因为这仅用于插件的 UserDetailsS​​ervice。 s2-quickstart 将为您提供登录/注销控制器和 GSP,因此您可以使用虚拟包运行它并删除生成的域类。 再次感谢,非常有帮助。最后一个问题,您说“我将 id 保留在 UserDetails 中,以便轻松重新加载源用户”。您如何知道何时在 UserDetails 中重新加载用户?我假设您不会在每次调用方法时都这样做? 它不是来自 UserDetails,它通常在初始登录后的控制器请求中。 UserDetails 很容易访问,因为它是身份验证“主体”,所以如果 id 在那里,您可以通过“User.get(principal.id)”获取用户。您也可以执行“User.findByUsername(principal.username)”,但正如我所说,只有在用户名上有索引时才有效。 知道了,在我的情况下,用户名有一个唯一索引,所以我不需要将id 添加到用户详细信息中。我认为使用 id 而不是 username 可能会有一些缓存优势,但目前我更感兴趣的是让事情正常工作而不是优化性能。 【参考方案1】:

请记住,Spring Security 并不关心数据来自哪里,它只需要一个 UserDetails 实例,在使用 DAO 身份验证提供程序进行身份验证时,它可以来自任何地方。使用域类和数据库表很方便,但这只是一种方法。做对您的数据有用的事情。最后,您需要的是一个新的 GrailsUser(或其他一些 impl)实例,其中包含 usernamepassword 集、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 类等)非常小,只有几个字符串和布尔值。

【讨论】:

非常有帮助,非常感谢。我在上面发布了一些后续问题 我听从了你的建议,效果很好——非常感谢【参考方案2】:

在 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,但在我的情况下不需要

以上是关于将现有的域类与 Spring Security 插件一起使用的主要内容,如果未能解决你的问题,请参考以下文章

可以将现有的 Java 类与 Actor 集成吗?

grails 和 spring security acl:仅显示域类的一些实例

grails spring security ui在用户域类上没有电子邮件字段

将现有的 Spring 应用程序转换为 Spring-Boot

你没有权限... spring security shiro

Grails Spring-Security - 如何比较密码-