简体   繁体   中英

How to fetch user role from DB using LDAP login using Spring Boot

Hi I am novice to Spring Boot and LDAP. I am facing problems!

I could login via LDAP but I cant fetch user role which is stored in my Database. I do get the following error:

org.springframework.security.ldap.userdetails.LdapUserDetailsImpl cannot be cast to com.test.rnd.geo.web.dto.CustomUser

I had tried this:

    private CustomUser getUserDetails() {
        CustomUser userDetails = (CustomUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        LOGGER.info("Deatils:  "+userDetails);
        LOGGER.info("UserName: " + userDetails.getUsername());
        LOGGER.info("Auth Token: " + userDetails.getAuthToken());
        LOGGER.info("User Role size: " + userDetails.getAuthorities().size());
        LOGGER.info("User Role : " + userDetails.getAuthorities());


        return userDetails;
    }

I am getting error to fetch this getUserDetails() function.

        CustomUser customUser = getUserDetails();

Here is my CustomUser Class:

@Component
@Scope("session")
public class CustomUser implements UserDetails {


    private String username;
    private String password;
    private List<RoleEntity> authorities;
    private String authToken;

    public String getAuthToken() {
        return authToken;
    }

    public void setAuthToken(String authToken) {
        this.authToken = authToken;
    }

    public void setAuthorities(List<RoleEntity> authorities) {
        this.authorities = authorities;
    }



    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "CustomUser{" +
                "username='" + username + '\'' +
                ", password='****" + '\'' +
                ", authorities=" + authorities +
                ", AuthToken='" + authToken + '\'' +
                '}';
    }
}

Here's my WebSecurityConfig:

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth
                .authenticationProvider(customAuthenticationProvider)
                .ldapAuthentication()
                .userSearchFilter("(&(mail={0})(memberOf=CN=aa,OU=Security Group,OU=aa,DC=aa,DC=com))")
                .contextSource()
                .url("ldap://www.domain.com:3268/dc=v,dc=com")
                .managerDn("aaa")
                .managerPassword("aaa");

    }

Repository Class:

@Repository
public class UserAuthRepository {
    @Autowired
    private InterServiceConnector serviceConnector;
    @Autowired
    private httpsServiceConnector httpsServiceConnector;
    private static final org.slf4j.Logger LOG
            = LoggerFactory.getLogger(CustomAuthenticationProvider.class);


    @Value("${dmzAddress}")
    private String host;

    public CustomUser authenticateUser(String userName, String password) {
        CustomUser customUser = null;
        try {
            customUser = null;

            LOG.info("UserAuthRepository::authenticateUser:: userName" + userName);
//            LOG.info("UserAuthRepository::authenticateUser:: password ********");

            Request request = new Request();
            request.setUsername(userName);
            request.setPassword(password);

            Gson gson = new GsonBuilder().create();
            String jsonInput = gson.toJson(request);

//            LOG.info("UserAuthRepository::authenticateUser:: Authentication Token :: " + request.getAuthToken());

            String user = null;
            if (host.contains("http://")){
                user = serviceConnector.serviceCall(jsonInput, "portalLogin", "post", request.getAuthToken());
            }
            else{
                user = httpsServiceConnector.serviceCall(jsonInput, "portalLogin", "post");
            }
//            String user = serviceConnector.serviceCall(jsonInput, "portalLogin", "post", request.getAuthToken());
//          String user = httpsServiceConnector.serviceCall(jsonInput, "portalLogin", "post");
//            LOG.info("UserAuthRepository::authenticateUser:: user---->>>  " +  user);
            customUser = gson.fromJson(user, CustomUser.class);
            LOG.info("UserAuthRepository:: Custom User::  {}", customUser);
        } catch (JsonSyntaxException e) {
            LOG.error("UserAuthRepository :: authenticateUser :: Parse Exception", e);
        }
        return customUser;
    }



}

First, your CustomUser should implement LdapUserDetails and not UserDetails

You also need to subclass LdapUserDetailsMapper because Spring will call mapUserFromContext and this is where you can fetch whatever data you need from your database and create an instance of CustomUser.

@Component
public class CustomLdapUserDetailsMapper extends LdapUserDetailsMapper {

@Autowired
private IUserRepository userRepository;

@Autowired
private IUserRoleRepository userRoleRepository;

@Override
 public UserDetails mapUserFromContext(DirContextOperations ctx, String   username,
        Collection<? extends GrantedAuthority> authorities) {

 
    User user = userRepository.findOneByUsername(username);
    if (user == null) {
        throw new UsernameNotFoundException("Username not found");
    }
    UserDetails userDetails = super.mapUserFromContext(ctx, username, getAuthorities(user));
   //add a constructor for your CustomUser
    return new CustomUser(user.getEmail(), user.getFullName(),  (LdapUserDetails) userDetails);
}

}
private Set<GrantedAuthority> getAuthorities(User user) {
    Set<GrantedAuthority> authorities = new HashSet<>();

    //populate authorities/user roles from User (db entity) and UserRole entities

    for (UserRole userRole : userRoleRepository.findAllByUserId(user.getId())) {
        authorities.add(new SimpleGrantedAuthority(userRole.getRole.getName()));

    }

    return authorities;
}
}

And finally, tell your authentication provider to use this ldap mapper class (put this in your web security code)

@Autowired
private CustomLdapUserDetailsMapper customLdapUserDetailsMapper;     
.... 

customAuthenticationProvider.setUserDetailsContextMapper(customLdapUserDetailsMapper);

UPDATE: The CustomUser code might look something like this

public class CustomUser implements LdapUserDetails{

private  String email;

private String fullName;

private  String username;

private LdapUserDetails ldapUserDetails;

public CustomUser(String email, String fullName, LdapUserDetails  ldapUserDetails) {
    super();
    
    this.email = email;
    this.fullName = fullName;
    this.ldapUserDetails = ldapUserDetails;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return ldapUserDetails.getAuthorities();
}

@Override
public String getPassword() {
    return ldapUserDetails.getPassword();
}


@Override
public String getUsername() {
    return ldapUserDetails.getUsername();
}
//add all the other overrides and getter/setter methods

}

And the repository classes might look something like

   public interface IUserRepository extends JpaRepository<User, Integer> {

    public User findOneByUsername(String username);

    public User findOneById(Integer id);

    public User findOneByEmail(String email);

}


public interface IUserRoleRepository extends JpaRepository<UserRole, Integer> {

UserRole findOneByRoleName(String rolename);//role name like admin, user, mod etc

UserRole findOneById(Integer id);//role id
}

@dsp_user gave great solutions.

Now Based on your above codes I assume that your are using a function in your Repository Class that takes username and password as the params.

Do make similar another function that takes only username as param. Call that function in your CustomLdapUserDetailsMapper Class.

Try this code:

@Component
public class CustomLdapUserDetailsMapper extends LdapUserDetailsMapper {

    @Autowired
    private UserRepository userRepository;


    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {

        CustomUser user = userRepository.authenticationMethod(username);
        if (user == null) {
            throw new UsernameNotFoundException("User is not found!");
        }

        return user;
    }

}

I hope this would solve the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM