问题
I've been banging my head on a wall try to map a grails user authenticated against LDAP to the database Roles and Users in the application. Following the examples in this answer and the documentation.
If I don't include the bean ldap auth works, but there are many ties in the GORM to a user for creates and updates.
I keep reaching the point where my CustomUserDetails cannot cast to the UserDetails.
Message: Cannot cast object 'ldap_username' with class 'package.MdtUserDetails' to class 'org.springframework.security.core.userdetails.UserDetails'
The error occurs in the UserDetailsContextMapper Class, when calling MdtUserDetails:
import java.util.Collection;
import org.springframework.ldap.core.DirContextAdapter
import org.springframework.ldap.core.DirContextOperations
import org.springframework.security.authentication.DisabledException
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.GrantedAuthorityImpl
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper
import grails.plugin.springsecurity.SpringSecurityUtils
import package.User
import package.Role
import package.UserRole
class MdtUserDetailsContextMapper implements UserDetailsContextMapper {
private static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]
def dataSource
@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<GrantedAuthority> authority) {
username = username.toLowerCase()
User user = User.findByUsername(username)
String ldapName = ctx.originalAttrs.attrs['name']
String ldapEmail = ctx.originalAttrs.attrs['mail']
String splitName = ldapName.split(": ")[1]
String fullname = splitName.split(", ")[1] + " " + splitName.split(", ")[0]
String email = ldapEmail.split(": ")[1]
def roles
User.withTransaction {
if(!user){
user = new User(username: username, enabled: true, fullName: fullname, email: email).save(flush: true)
UserRole.create user, Role.findByAuthority('ROLE_USER'), true
roles = Role.findByAuthority('ROLE_USER')
}
else {
user = User.findByUsername(username)
user.fullName = fullname
user.email = email
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 MdtUserDetails(fullname, email, username, "", true, false, false, false, authorities) //the error is here...
return userDetails
}
@Override
public void mapUserToContext(UserDetails arg0, DirContextAdapter arg1) {
}
}
here is my resources.goovy
import package.MdtUserDetailsContextMapper
import package.MdtUserDetailsService
beans = {
ldapUserDetailsMapper(MdtUserDetailsContextMapper) {
dataSource = ref("dataSource")
}
UserDetailsService(MdtUserDetailsService)
}
config.groovy:
grails.plugin.springsecurity.ldap.context.managerDn = 'cn=MDT Apache,ou=ServiceAccounts,ou=Users,ou=MDT,dc=mdthq,dc=mt,dc=ads'
grails.plugin.springsecurity.ldap.context.managerPassword = '*******'
grails.plugin.springsecurity.ldap.context.server = 'ldap://server:389'
grails.plugin.springsecurity.ldap.authorities.groupSearchBase = 'ou=Groups,ou=MDT,dc=mdthq,dc=mt,dc=ads'
grails.plugin.springsecurity.ldap.search.base = 'ou=Users,ou=MDT,dc=mdthq,dc=mt,dc=ads'
grails.plugin.springsecurity.ldap.authorities.retrieveGroupRoles = true
grails.plugin.springsecurity.ldap.authorities.retrieveDatabaseRoles = true
grails.plugin.springsecurity.ldap.mapper.userDetailsClass = 'package.MdtUserDetails'
grails.plugin.springsecurity.ldap.authorities.ignorePartialResultException = true // typically needed for Active Directory
grails.plugin.springsecurity.ldap.search.filter="sAMAccountName={0}" // for Active Directory you need this
grails.plugin.springsecurity.ldap.search.searchSubtree = true
grails.plugin.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugin.springsecurity.ldap.search.attributesToReturn = ['mail', 'displayName', 'sAMAccountName'] // extra attributes you want returned; see below for custom classes that access this data
grails.plugin.springsecurity.ldap.authorities.groupSearchFilter = '(member:1.2.840.113556.1.4.1941:={0})'
//check against LDAP first, then Database
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider', 'daoAuthenticationProvider']
Then classes for UserDetails and UserDetailsService:
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority
import package.User
class MdtUserDetails extends User {
public MdtUserDetails(String fullName, String email, String username, String password, boolean enabled, boolean accountExpired,
boolean accountLocked, boolean passwordExpired, Collection<GrantedAuthority> authorities) {
super(username: username, password: password, email: email, fullName: fullName, enabled: enabled, accountExpired: accountExpired, accountLocked: accountLocked, passwordExpired: passwordExpired)
this.fullName = fullName
this.email = email
}
}
import package.User
import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException
import grails.plugin.springsecurity.SpringSecurityUtils
class MdtUserDetailsService 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 MdtUserDetails(user.fullName, user.email, user.username, user.password, user.enabled,
!user.accountExpired, !user.passwordExpired,
!user.accountLocked, authorities ?: NO_ROLES)
} as UserDetails
}
}
Updated UserDetails:
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.ldap.userdetails.LdapUserDetails
import package.Role
import package.User
class MdtUserDetails extends User implements LdapUserDetails{
final String email
final String fullName
public MdtUserDetails(String fullName, String email, String username, String password, boolean enabled, boolean accountExpired,
boolean accountLocked, boolean passwordExpired, Collection<GrantedAuthority> authorities) {
//super(username: username)
//super(username: username, password: password, email: email, fullName: fullName, enabled: enabled, accountExpired: accountExpired, accountLocked: accountLocked, passwordExpired: passwordExpired, authorties: authorities)
this.fullName = fullName
this.email = email
}
@Override
public Set<Role> getAuthorities(){
return super.getAuthorities()
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return false;
}
@Override
public String getDn() {
// TODO Auto-generated method stub
return null;
}
}
EDIT
Finally figured this out!
in the UserDetails class I was extending my own user class:
import package.User
class MdtUserDetails extends User {
Instead I needed to extend the springsecurity user class from here :
import org.springframework.security.core.userdetails.User
回答1:
MdtUserDetails
needs to implement the UserDetails
interface. Try something like this:
class MdtUserDetails extends User implements LdapUserDetails {
... //everything required by the interface
}
来源:https://stackoverflow.com/questions/21335251/grails-spring-security-ldap