如何将LDAP用户与Grails中Spring Security创建的PERSON表集成?

时间:2013-04-29 20:27:06

标签: grails spring-security grails-plugin spring-ldap spring-security-ldap

我们正在创建一个grails aplication,我们希望用户使用他们的Active Directory凭据登录。此外,我们希望为此应用程序的业务所有者提供控制谁可以访问某些链接(操作)的能力。因此,我们在grails应用程序中使用以下插件:

  1. Spring Security Core
  2. Spring Security LDAP
  3. Spring Security UI
  4. 因为我们希望授权业务用户在必要时动态创建具有特定权限(操作)的自定义角色,我们认为最好的Spring安全配置是基于Requestmap数据库的方法

    到目前为止,我们完成了以下工作:

    • 我们能够成功验证Active Directory。
    • 我们还可以通过spring-security-ui插件的UI界面为不同的角色(ROLE_XXX)创建不同的请求映射

    问题/问题

    spring-security-core插件创建了以下表格:

    • PERSON
    • AUTHORITY
    • PERSON_AUTHORITY
    • REQUESTMAP

    这些是支持角色创建的表,即角色的URL分配。但是,Person_Authoritity表作为约定名称意味着它是PERSON和AUTHORITY(ROLE)之间的多对多关系,因为一个人可能有多个角色。我的问题是我没有Person,因为该人已存在于Active Directory(外部源)中,并且未在应用程序中创建。

    有没有办法让经过身份验证的用户成为PERSON? Spring安全解决方案需要Person行或对象,但您更喜欢引用它。

    我也在这里发布了这个问题:

    http://grails.1312388.n4.nabble.com/Issues-integrating-LDAP-Authentication-with-Requestmap-to-Secure-URLs-td4644040.html

    谢谢,

1 个答案:

答案 0 :(得分:6)

因此,您需要将AD用户实际映射到Person。

以下是src / groovy中需要的3个类。显然可以根据需要修改它们:

    package yourpackagename

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

    class CustomUserDetails extends GrailsUser{
        final String firstName
        final String lastName

        CustomUserDetails(String username, String password, boolean enabled,
                          boolean accountNonExpired, boolean credentialsNonExpired,
                          boolean accountNonLocked,
                          Collection<GrantedAuthority> authorities,
                          long id, String firstName, String lastName) {
            super(username, password, enabled, accountNonExpired,
                    credentialsNonExpired, accountNonLocked, authorities, id)

            this.firstName = firstName
            this.lastName = lastName
        }
    }

package yourpackagenamehere

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

class CustomUserDetailsService implements GrailsUserDetailsService {

    /**
     * Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
     * we give a user with no granted roles this one which gets past that restriction but
     * doesn't grant anything.
     */
    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 CustomUserDetails(user.username, user.password, user.enabled,
                    !user.accountExpired, !user.passwordExpired,
                    !user.accountLocked, authorities ?: NO_ROLES, user.id,
                    user.firstName, user.lastName)
        } as UserDetails
    }
}

package yourpackagenamehere

import groovy.sql.Sql

import org.springframework.ldap.core.DirContextAdapter
import org.springframework.ldap.core.DirContextOperations
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper
import org.springframework.security.core.authority.GrantedAuthorityImpl
import org.springframework.security.core.GrantedAuthority
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.authentication.DisabledException

class CustomUserDetailsContextMapper implements UserDetailsContextMapper {

    private static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]

    def dataSource

    @Override
    public CustomUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<GrantedAuthority> authority) {

        username = username.toLowerCase()

        User user = User.findByUsername(username)

        String firstName = ctx.originalAttrs.attrs['givenname'].values[0]
        String lastName = ctx.originalAttrs.attrs['sn'].values[0]


        def roles

        User.withTransaction {

            if(!user){
                user = new User(username: username, enabled: true, firstName: firstName, lastName: lastName)
                user.save(flush: true)
            }
            else {
                user = User.findByUsername(username)
                user.firstName = firstName
                user.lastName = lastName
                user.save(flush: true)
            }

            roles = user.getAuthorities()
        }

        if ( !user.enabled )
            throw new DisabledException("User is disabled", username)


        def authorities = roles.collect { new GrantedAuthorityImpl(it.authority) }
        authorities.addAll(authority)
        def userDetails = new CustomUserDetails(username, user.password, user.enabled, false, false, false, authorities, user.id, user.firstName, user.lastName)

        return userDetails
    }

    @Override
    public void mapUserToContext(UserDetails arg0, DirContextAdapter arg1) {
    }
}

在spring / resources.groovy中配置:

import yourpackagenamehere.CustomUserDetailsService
import yourpackagenamehere.CustomUserDetailsContextMapper
beans = {
    userDetailsService(CustomUserDetailsService)

    ldapUserDetailsMapper(CustomUserDetailsContextMapper) {
        dataSource = ref("dataSource")
    }
}

在Config.groovy下,这是我的设置:

grails.plugins.springsecurity.ldap.context.managerDn = 'CN=username,OU=People,DC=foo,DC=com'
grails.plugins.springsecurity.ldap.context.managerPassword = 'password'
grails.plugins.springsecurity.ldap.context.server = 'ldap://foo.com:389/'
grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException = true
grails.plugins.springsecurity.ldap.search.base = 'ou=People,dc=foo,dc=com'
grails.plugins.springsecurity.ldap.search.filter="sAMAccountName={0}"
grails.plugins.springsecurity.ldap.search.searchSubtree = true
grails.plugins.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugins.springsecurity.ldap.search.attributesToReturn = null
grails.plugins.springsecurity.providerNames = ['ldapAuthProvider', 'anonymousAuthenticationProvider']
grails.plugins.springsecurity.ldap.mapper.userDetailsClass = 'CustomUserDetails'

grails.plugins.springsecurity.ldap.authorities.retrieveGroupRoles = true
grails.plugins.springsecurity.ldap.authorities.retrieveDatabaseRoles = true
grails.plugins.springsecurity.ldap.authorities.groupSearchBase ='dc=foo,dc=com'
grails.plugins.springsecurity.ldap.authorities.groupSearchFilter = 'member={0}'