techPackets
techPackets

Reputation: 4516

java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to model.User

I am using Spring Security in my application. I need loggedIn user details in the controllers of my application.

For that I am using this code

User loggedInUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();

But on running this code I get a classcastexception

java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to model.User

To fix this I referred to this article

Initially I used a CustomUserServiceDetails class

@Service("myUserDetailService")
@Transactional
public class CustomUserDetailsService implements UserDetailsService {

    private static final Logger logger = Logger.getLogger(CustomUserDetailsService.class);

    @Autowired
    private UserDAO userDAO;

    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException, DataAccessException {
        // returns the get(0) of the user list obtained from the db
        User domainUser = userDAO.getUser(name);
        logger.debug("User fetched from database in loadUserByUsername method " + domainUser);

        Set<Role> roles = domainUser.getRole();
        logger.debug("role of the user" + roles);

        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        for(Role role: roles){
            authorities.add(new SimpleGrantedAuthority(role.getRole()));
            logger.debug("role" + role + " role.getRole()" + (role.getRole()));
        }

        boolean credentialNonExpired = true;

        return new org.springframework.security.core.userdetails.User(domainUser.getProfileName(), domainUser.getPassword(), domainUser.isAccountEnabled(),
                domainUser.isAccountNonExpired(), credentialNonExpired, domainUser.isAccountNonLocked(),authorities);

    }

}

But after referring to the article I removed the setting of GrantedAuthorities from here and moved it to my User class. Implemented spring-security UserDetails class in my User class

Now I have an extra property in my User class

@Entity
@Table(name = "user")
public class User implements UserDetails {
    private Collection<GrantedAuthority> authorities;

with a setMethod

public void setAuthorities(Set<Role> roles) {
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        for(Role role: roles){
            authorities.add(new SimpleGrantedAuthority(role.getRole()));}
    }

A. I am not sure how to map this property to the database. The existing User table schema doesn't contain a GrantedAuthority column besides It's not even a primitive type. I am using Hibernate for object mapping. Can anyone advice me the correct approach to obtain the user class info in the controllers?

B. I also considered the approach of extending the spring's User class and overloading the constructor of my User class. But then every time I initialize my User anywhere in the code I have to provide all the constructors parameters which is not good at all.

Upvotes: 12

Views: 72471

Answers (3)

techPackets
techPackets

Reputation: 4516

Fixed the issue

Solution

Created a CustomUserDetail class which implements Spring's UserDetails interface. Injected my model User class in it.

public class CustomUserDetail implements UserDetails{

    private static final long serialVersionUID = 1L;
    private User user;

    Set<GrantedAuthority> authorities=null;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

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

    public void setAuthorities(Set<GrantedAuthority> authorities)
    {
        this.authorities=authorities;
    }

    public String getPassword() {
        return user.getPassword();
    }

    public String getUsername() {
        return user.getProfileName();
    }

    public boolean isAccountNonExpired() {
        return user.isAccountNonExpired();
    }

    public boolean isAccountNonLocked() {
        return user.isAccountNonLocked();
    }

    public boolean isCredentialsNonExpired() {
        return user.isCredentialsNonExpired();
    }

    public boolean isEnabled() {
        return user.isAccountEnabled();
    }

}

CustomUserServiceDetails

public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserDAO userDAO;

    public CustomUserDetail loadUserByUsername(String name) throws UsernameNotFoundException, DataAccessException {
        // returns the get(0) of the user list obtained from the db
        User domainUser = userDAO.getUser(name);


        Set<Role> roles = domainUser.getRole();
        logger.debug("role of the user" + roles);

        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        for(Role role: roles){
            authorities.add(new SimpleGrantedAuthority(role.getRole()));
            logger.debug("role" + role + " role.getRole()" + (role.getRole()));
        }

        CustomUserDetail customUserDetail=new CustomUserDetail();
        customUserDetail.setUser(domainUser);
        customUserDetail.setAuthorities(authorities);

        return customUserDetail;

    }

}

In my controller method

CustomUserDetail myUserDetails = (CustomUserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Integer userId=myUserDetails.getUser().getUserId(); //Fetch the custom property in User class

Upvotes: 13

B378
B378

Reputation: 1117

Instead of using

User loggedInUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();

try this

Authentication loggedInUser = SecurityContextHolder.getContext().getAuthentication();
String username = loggedInUser.getName();

References: https://www.mkyong.com/spring-security/get-current-logged-in-username-in-spring-security/

Upvotes: 14

The method .getPrincipal() returns the object created and returned it in the method loadUserByUsername.

If you want an User you must return in the method loadUserByUsername an User, not an org.springframework.security.core.userdetails.User

Upvotes: 2

Related Questions