Bien sûr. Vous devez mettre en œuvre sur mesure AuthProvider
SecurityConfig.groovy:
security {
providerNames = ['ldapAuthProvider']
}
Ldap Auth Fournisseur:
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)
}
}
}
Et appUserService.updateLocalUser(ldapUser)
vous devez créer/modifier votre objet de domaine et persistent dans la base de données.
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.")
}
MISE À JOUR # 1
Vous pouvez mettre en œuvre UserDetailsService personnalisée:
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); } }
Ai-je vraiment besoin de mettre en œuvre le LdapProvider personnalisé? En utilisant le standard dans Spring-Security, il semble que je peux obtenir les données d'utilisateur/rôle pertinentes à partir du magasin LDAP. J'ai juste besoin de coller dans un UserDetailsService personnalisé ou quelque chose pour passer la partie où il se trouve dans la base de données pour l'objet utilisateur. J'ai inclus certaines des instructions de débogage dans la question –
Le code ci-dessus est du projet réel avec quelques cleaups. J'ai réfléchi à votre commentaire et je pense que vous pouvez utiliser UserDetailsService personnalisé. J'ai mis à jour la réponse. – uthark
Oui, je l'ai trié avec un UserDetailsService personnalisé. La seule chose est qu'il existe une deuxième méthode loadUserByUsername, qui prend le nom d'utilisateur et un booléen qui représente si rechercher des rôles à partir de la base de données. Dans mon cas, la deuxième méthode était appelée, et je devais la remplacer. –