【问题标题】:Auto-creating User details with Grails and LDAP使用 Grails 和 LDAP 自动创建用户详细信息
【发布时间】:2011-02-03 04:25:12
【问题描述】:

我正在为 Grails 使用 Acegi Security 插件,并通过 LDAP 进行身份验证。

应用程序日志显示,在登录时,我们可以通过 LDAP 验证用户并获取他们的角色,但登录失败,因为在应用程序的数据库中找不到用户详细信息。

如果一个基本的用户详细信息域对象尚不存在,有没有办法自动创建和保存?

-- 更新
以下是我目前看到的相关调试条目

DEBUG populator.DefaultLdapAuthoritiesPopulator  - Roles from search: [Role1, Role2, etc]
ERROR springsecurity.GrailsDaoImpl  - User not found: MyUserName
DEBUG rememberme.TokenBasedRememberMeServices  - Interactive login attempt was unsuccessful.

【问题讨论】:

    标签: java grails ldap spring-security


    【解决方案1】:

    当然。 您需要实现自定义 AuthProvider

    SecurityConfig.groovy:

    security {
       providerNames = ['ldapAuthProvider']
    }
    

    Ldap 身份验证提供者:

    import domain.user.AppUser
    import org.apache.commons.codec.digest.DigestUtils
    import org.apache.log4j.Logger
    import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserImpl
    import org.springframework.security.BadCredentialsException
    import org.springframework.security.GrantedAuthority
    import org.springframework.security.GrantedAuthorityImpl
    import org.springframework.security.providers.UsernamePasswordAuthenticationToken
    import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider
    import org.springframework.security.userdetails.UserDetails
    
    /**
     * Authentication provider that checks user credentials against LDAP
     */
    class LdapAuthProvider extends AbstractUserDetailsAuthenticationProvider {
        private static final Logger log = Logger.getLogger(LdapAuthProvider.class)
    
        def appUserService
    
        /**
         * Checks password hash stored in the session with password in authentication token.
         */
        protected void additionalAuthenticationChecks(UserDetails details,
            UsernamePasswordAuthenticationToken authentication) {
    
            if (details.password != DigestUtils.md5Hex(authentication.credentials)) {
                throw new BadCredentialsException(details.username)
            }
        }
    
        /**
         * Retrieves user from LDAP,
         * checks credentials,
         * updates local copy of user data,
         * returns user details.
         */
        protected UserDetails retrieveUser(String login, UsernamePasswordAuthenticationToken authentication) {
            AppUser.withTransaction {
                log.debug("Trying to retrieve user \"$login\"...")
                def password = authentication.credentials?.toString()
    
                def ldapUser = appUserService.findLdapUser(login)
                if (!(password && ldapUser?.authenticate(password))) {
                    log.debug("Can't authenticate \"$login\"")
                    throw new BadCredentialsException(login)
                }
    
                AppUser localUser = AppUser.findByLogin(login, [cache: true])
    
                if (!localUser) {
                    log.debug("Can't authenticate \"$login\"")
                    localUser = appUserService.updateLocalUser(ldapUser)
                }
    
                log.debug("User \"$login\" is authenticated.")
                def authorities = localUser.collectAuthorities().collect {String authority ->
                    log.debug("\thas right \"$authority\"")
                    new GrantedAuthorityImpl(authority)
                }
    
                def userDetails = new AppUser();
                userDetails.setAssignedTemplate(localUser.assignedTemplate)
                userDetails.setFullName(localUser.getFullName())
                userDetails.setLogin(localUser.getLogin())
                userDetails.setEmail(localUser.getEmail())
                userDetails.setDisabled(localUser.getDisabled())
                userDetails.setManager(localUser.getManager())
                userDetails.setRoles(new HashSet(localUser.getRoles()))
    
    
                log.debug("Retrieving user \"$login\" is completed.")
                return new GrailsUserImpl(userDetails.login, DigestUtils.md5Hex(password), true, true, true, true,
                    authorities.toArray(new GrantedAuthority[authorities.size()]), userDetails)
            }
        }
    }
    

    在appUserService.updateLocalUser(ldapUser) 中,您需要创建/修改您的域对象并保存在数据库中。

    AppUser updateLocalUser(LdapUser ldapUser) {
        def login = ldapUser.login
        log.debug("Start updating local user ${login}...")
        def localUser = AppUser.findByLogin(login, [cache: true]) ?: new AppUser()
        if (localUser.id) {
            log.debug("user $login was found in local DB")
            if (localUser.disabled ^ ldapUser.isDisabled()) {
                log.debug("...user ${login} has been ${localUser.disabled ? 'activated' : 'disabled'}...")
            }
        } else {
            log.debug("user $login is new")
        }
        localUser.login = login
        localUser.email = ldapUser.email
        localUser.fullName = ldapUser.fullName ?: login
        localUser.disabled = ldapUser.isDisabled();
        localUser.roles?.clear()
        ldapUser.memberOf.collect { Role.findByLdapName(it, [cache: true]) }.each {role ->
            if (role) {
                localUser.addToRoles(role)
            }
        };
        localUser.save(flush: true)
        log.debug("Update local user $login is complete.")
    }
    

    更新 #1

    您可以实现自定义 UserDetailsS​​ervice:

    package com.foo.bar;
    
    import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UserDetailsService;
    
    public class MyUserDetailsService implements UserDetailsService {
    
    public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException {
    
    // lookup user and data
    
    return new MyUserDetails(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id, fullName); } }
    

    【讨论】:

    • 我真的需要实现自定义的 LdapProvider 吗?使用 Spring-Security 中的标准,似乎我可以从 LDAP 存储中获取相关的用户/角色数据。似乎我只需要坚持自定义 UserDetailsS​​ervice 或其他东西来跳过它在数据库中查找用户对象的部分。我在问题中包含了一些调试语句
    • 上面的代码来自实际项目,有一些清理。我考虑了您的评论,并认为您可以使用自定义 UserDetailsS​​ervice。我已经更新了答案。
    • 是的,我使用自定义 UserDetailsS​​ervice 对其进行了排序。唯一的事情是有第二个 loadUserByUsername 方法,它接受用户名和一个布尔值,表示是否从数据库中查找角色。在我的例子中,第二种方法被调用了,我需要覆盖那个方法。
    猜你喜欢
    • 1970-01-01
    • 2016-12-14
    • 2010-12-22
    • 1970-01-01
    • 2015-06-03
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多