Grails Spring Security 扩展用户属性

Posted

技术标签:

【中文标题】Grails Spring Security 扩展用户属性【英文标题】:Grails Spring Security extending User attributes 【发布时间】:2014-02-27 03:50:48 【问题描述】:

我有一个项目将成为现有 ERP 应用程序的附加组件。我已经通过基本的 Spring Security 设置让 SSO 正常工作(在此处查看我的票证:Grails and CAS Basic Setup)。另外,我没有使用 s2-quickstart。

我还是 Java 开发的新手,所以请多多包涵……我一直在查看文档和示例,但在某些方面我对所看到的内容更加困惑。我有几个问题:

1) 此设置允许我访问已登录用户的用户名,但除此之外没有其他权限吗?如何访问 User 对象以访问这些属性?现在,我正在使用:

def userDetails = springSecurityService.principal
then: userDetails ?.getUsername() to get the username
and: userDetails ?.getAuthorities() to get roles

一切正常,除了这是访问这些属性的最佳方式吗? 即,用户详细信息中是否还有其他属性,我如何访问这些属性?

也许这个问题的答案取决于我接下来要做什么......

2) 我正在使用 CAS 进行身份验证,但现在我想从 LDAP 中提取一些附加属性。我曾尝试创建一个像 http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/userDetailsService.html 这样的自定义 UserDetailsS​​ervice,但即使在我克服了初始编译错误之后,它似乎仍然需要 User 域类。如果我想用附加值扩展 UserDetails,我必须实现一个 User 域类吗?

在我的情况下,我真的只想要 1 个 LDAP 属性 - 而这一切似乎都需要大量提升才能获得它。我看了又看,但找不到一个很好的工作/简单的例子……很多人都参与了 Gorm 或安装了 s2-quickstart。

蒂亚,

豆子

更新

这是我的配置...看到最后的错误:

Config.groovy 片段

    grails.plugins.springsecurity.providerNames = ['casAuthenticationProvider']

grails.plugins.springsecurity.cas.active = true
grails.plugins.springsecurity.cas.sendRenew = false
grails.plugins.springsecurity.cas.serverUrlEncoding = 'UTF-8'
grails.plugins.springsecurity.cas.key = 'changeme'

grails.plugins.springsecurity.cas.loginUri = '/login'
grails.plugins.springsecurity.cas.serviceUrl = '$grails.serverURL/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.serverUrlPrefix = 'https://cas2.mydomain.com:8443/cas'
grails.plugins.springsecurity.cas.proxyCallbackUrl = '$grails.serverURL/secure/receptor'
grails.plugins.springsecurity.cas.proxyReceptorUrl = '/secure/receptor'

grails.plugins.springsecurity.logout.afterLogoutUrl = 'https://cas2.mydomain.com:8443/cas/logout?service=' + appProtocol + '://cas2.mydomain.com:' + appPort + '/' + appName + '/'

grails.plugins.springsecurity.cas.artifactParameter = 'ticket'
grails.plugins.springsecurity.cas.serviceParameter = 'service'
grails.plugins.springsecurity.cas.filterProcessesUrl = '/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.useSingleSignout = true

grails.server.loopback.url = ""

//Spring Security Core Config

grails.plugins.springsecurity.rejectIfNoRule = false // V.044::to fix redirect loop::true
grails.plugins.springsecurity.securityConfigType = "InterceptUrlMap"
/*
 * Order matters...put the most restrictive first
 */
grails.plugins.springsecurity.interceptUrlMap = [
    '/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/logout/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/secure/receptor': ['IS_AUTHENTICATED_ANONYMOUSLY'],  // <- allows CAS to contact the receptor
    '/protected/**': ['IS_AUTHENTICATED_FULLY'],
    '/unprotected/**': ['IS_AUTHENTICATED_ANONYMOUSLY','IS_AUTHENTICATED_FULLY'],
    '/filtered/edit':      ["hasRole('ROLE_XYZ')"],
    '/filtered/create': ["authentication.uid == 'criderk'"],
    '/filtered/list': ["hasRole('ROLE_44808')"],
    '/filtered/index': ["hasRole('ROLE_44808')"],
    '/': ['IS_AUTHENTICATED_ANONYMOUSLY','IS_AUTHENTICATED_FULLY']

]


grails.plugins.springsecurity.ldap.search.attributesToReturn = ['uid','mail']

grails.plugins.springsecurity.userLookup.userDomainClassName = 'basecas_04.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'basecas_04.UserRole'
grails.plugins.springsecurity.authority.className = 'basecas_04.Role'

resources.groovy

// Place your Spring DSL code here
beans = 


    // load ldap roles from spring security
    initialDirContextFactory(org.springframework.security.ldap.DefaultSpringSecurityContextSource,
        "ldap://xx.xx.xx.xx:389")
        userDn = "cn=admin,dc=mydomain,dc=com"
        password = "pw"
    

    ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
        "ou=employees,dc=mydomain,dc=com", "uid=0", initialDirContextFactory)

    

    ldapAuthoritiesPopulator(org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator,
        initialDirContextFactory,"ou=groups,dc=mydomain,dc=com")
          groupRoleAttribute = "gidNumber"
          groupSearchFilter = "memberUid=1"
          searchSubtree = true
          rolePrefix = "ROLE_"
          convertToUpperCase = true
          ignorePartialResultException = true
    

    ldapUserDetailsService(org.springframework.security.ldap.userdetails.LdapUserDetailsService,
    ldapUserSearch,
    ldapAuthoritiesPopulator)

    userDetailsService(basecas_04.CustomUserDetailsService)



CustomUserDetails.groovy

package basecas_04

import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.User

class MyUserDetails extends GrailsUser    

    final String mail   

    MyUserDetails(String username, String password, boolean enabled,
                 boolean accountNonExpired, boolean credentialsNonExpired,
                 boolean accountNonLocked,
                 Collection<GrantedAuthority> authorities,
                 long id, String mail) 

    super(username, password, enabled, accountNonExpired,
            credentialsNonExpired, accountNonLocked, authorities, id)      

    this.mail = mail
   

MyUserDetailsS​​ervice.groovy#(这是在服务中...?...还是在 src/groovy 中?)

package basecas_04


import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser  import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserDetailsService import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils  import org.springframework.security.core.authority.GrantedAuthorityImpl  import org.springframework.security.core.userdetails.UserDetails  import org.springframework.security.core.userdetails.UsernameNotFoundException


import basecas_04.User

class CustomUserDetailsService implements GrailsUserDetailsService         static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]            UserDetails loadUserByUsername(String username, boolean loadRoles)      throws UsernameNotFoundException                   return loadUserByUsername(username)                        UserDetails loadUserByUsername(String username)     throws UsernameNotFoundException              
                User.withTransaction  status ->   
                                User user = User.findByUsername(username)                       if (!user)                              throw new UsernameNotFoundException('User not found', username)                 

                        def authorities = user.authorities.collect 
                new GrantedAuthorityImpl(it.authority)                                   
                        return new MyUserDetails(user.username, user.password, user.enabled, !user.accountExpired,                 
                !user.passwordExpired, !user.accountLocked,                 
                authorities ?: NO_ROLES, user.id, user.mail)                                  

User.groovy(域类)

class User 

    transient springSecurityService

    String username
    String password
    String mail
    //String displayName
    boolean enabled
    boolean accountExpired
    boolean accountLocked
    boolean passwordExpired

    static constraints = 
        username blank: false, unique: true
        password blank: false
    

    static mapping = 
        password column: '`password`'
    

    Set<Role> getAuthorities() 
        UserRole.findAllByUser(this).collect  it.role  as Set
    

    def beforeInsert() 
        encodePassword()
    

    def beforeUpdate() 
        if (isDirty('password')) 
            encodePassword()
        
    

    protected void encodePassword() 
        password = springSecurityService.encodePassword(password)
    

我遇到了错误

2014-02-18 08:37:48,106 [http-apr-8444-exec-9] 错误错误。GrailsExceptionResolver - 处理请求时发生 MissingPropertyException:[GET] /basecas_04/protected/list 没有这样的属性:类的 id:org.springframework.security.ldap.userdetails.LdapUserDetailsImpl 可能的解决方案:dn。堆栈跟踪如下: groovy.lang.MissingPropertyException:没有这样的属性:类的ID:org.springframework.security.ldap.userdetails.LdapUserDetailsImpl 可能的解决方案:dn 在 grails.plugins.springsecurity.SpringSecurityService.getCurrentUser(SpringSecurityService.groovy:80) 在 basecas_04.ProtectedController.list(ProtectedController.groovy:22) 在 grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195) 在 grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63) 在 org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:65) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 在 java.lang.Thread.run(Thread.java:722)

对此有任何想法:

没有这样的属性:类的id:org.springframework.security.ldap.userdetails.LdapUserDetailsImpl

【问题讨论】:

浏览您的最新编辑,您似乎缺少ldapUserDetailsService bean。此外,您还需要在src/groovy/ 下自定义UserDetailsService——这不是真正的Grails 服务。最后,另一个可能有帮助的链接:pwu-developer.blogspot.com/2012/02/… 好的,我尽我所能添加了 ldapUserDetailsS​​ervice,并将 CustomUserDetailsS​​ervice 和 MyUserDetails 代码移动到 src/groovy...我已经用我当前的配置更新了上面的内容,但在登录后仍然出现错误进入我的 CAS 页面。 【参考方案1】:

在 Spring Security bean 迷宫中导航可能有点困难。从好的方面来说,一旦你掌握了方向,这是一个非常强大和灵活的 bean 迷宫 :)

对于 #1,您可以随意覆盖默认的 UserDetails 类 使用您自己的自定义实现,添加任何额外的 您需要的属性。为此,您需要定义一个自定义 UserDetailsService 如核心插件文档中所述 here.

对于 #2,此链接可能会有所帮助: http://swordsystems.com/2011/12/21/spring-security-cas-ldap/

【讨论】:

一件事我挂了,为了扩展 UserDetails,我必须有一个用户域类吗? 不,您不一定需要User 域类。您自定义的UserDetailsService 只需要返回一个符合UserDetails 接口的对象即可。您可以随意定义和构造这个UserDetails 对象(即,不依赖User 域类)。 Burt Beckwith 在这里提供了更多提示:***.com/questions/6899566/…【参考方案2】:

您的答案可能就在这里:spring-security-ldap(如果您正在使用它,因为我相信您也在使用它?):https://github.com/grails-plugins/grails-spring-security-ldap/blob/master/src/java/grails/plugin/springsecurity/ldap/userdetails/GrailsLdapUserDetailsManager.java - t

快速搜索:

security.ldap.userdetails.LdapUserDetailsManager

返回:

http://docs.spring.io/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.html#loadUserByUsername%28java.lang.String%29

按用户名加载用户

public UserDetails loadUserByUsername(字符串用户名) 抛出 UsernameNotFoundException, 数据访问异常

从接口复制的描述:UserDetailsS​​ervice 根据用户名定位用户。在实际实现中,搜索可能不区分大小写,或者不区分大小写,具体取决于实现实例的配置方式。在这种情况下,返回的 UserDetails 对象的用户名可能与实际请求的用户名不同。

Specified by:
    loadUserByUsername in interface UserDetailsService

Parameters:
    username - the username identifying the user whose data is required. 
Returns:
    **a fully populated user record (never null)** 

【讨论】:

以上是关于Grails Spring Security 扩展用户属性的主要内容,如果未能解决你的问题,请参考以下文章

Spring-security/Grails 应用程序 - 未调用自定义 WebSecurity 配置

Grails Spring Security 使用电子邮件登录

Grails spring security、servlet 过滤器和响应

在 Grails 中使用自己的方法扩展 SpEL?

Grails - 卸载 Spring Security Core

Grails - grails-spring-security-rest - 无法从 application.yml 加载 jwt 机密