SPRING: Add custom user details to spring security user
Asked Answered
T

6

27

I'm currently working on a Spring MVC application and I need to add a custom field to my Spring Security log-in user right when I log in (I insert username, password, custom value). This value needs to be available on everywhere when the user is logged in (e.g. via pricipal.getValue).

I read a lot about custom user classes and custom services, but can't really find a working solution for my problem...

Any help would be great!

Thorn answered 20/1, 2017 at 18:27 Comment(0)
B
30

Just like Avinash said, you can make your User class implement UserDetails and you can also implement UserDetailsService and override corresponding methods to return the custom User object:

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    //get user from the database, via Hibernate
    @Autowired
    private UserDao userDao;

    @Transactional(readOnly=true)
    @Override
    public UserDetails loadUserByUsername(final String username)
        throws UsernameNotFoundException {
//CUSTOM USER HERE vvv
        User user = userDao.findByUserName(username);
        List<GrantedAuthority> authorities =
                                      buildUserAuthority(user.getUserRole());
//if you're implementing UserDetails you wouldn't need to call this method and instead return the User as it is
        //return buildUserForAuthentication(user, authorities);
return user;

    }

    // Converts user to spring.springframework.security.core.userdetails.User
    private User buildUserForAuthentication(user,
        List<GrantedAuthority> authorities) {
        return new User(user.getUsername(), user.getPassword(),
            user.isEnabled(), true, true, true, authorities);
    }

    private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {

        Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();

        // add user's authorities
        for (UserRole userRole : userRoles) {
            setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
        }

        List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);

        return Result;
    }

}

And you just configure your WebConfigurerAdapter using the custom UserdetailsService :

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
UserDetailsService userDetailsService;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    //authorization logic here ...
}

    @Bean
    public PasswordEncoder passwordEncoder(){
        // return preferred PasswordEncoder ...//
    }


}

Here, a sample of a custom UserDetails implementation: custom UserDetails

Bumgardner answered 20/1, 2017 at 21:19 Comment(1)
How do you setup Hibernate with the authorities set in the UserDao class?Mcilwain
V
12

Create your class implementing UserDetails interface.

public class User implements UserDetails {
    // Your user properties
    // implement methods
}

And then, once authenticated, you can access this object anywhere in the project like this.

User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Valeric answered 20/1, 2017 at 18:38 Comment(1)
hm i dont really understand... i made a user class with my custom string field. a bunch of mehtods are generated like @Override public String getPassword() { // TODO Auto-generated method stub return null; } how can i then use the "User" class for spring security? or does it work auomaticly? i think a example code would help a lot :/Thorn
N
4

I had worked on this in the following way.

AccountDetailsService.java

@Service
public class AccountDetailsService implements UserDetailsService {

    @Autowired
    AccountRepository accountRepository;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException, JSONException {
        return loadUser(s);
    }

    public UserDetails loadUserByUsernameWithoutCredentials(String s) throws UsernameNotFoundException, JSONException {
        CustomUserDetails customUserDetails=loadUser(s);
        if (customUserDetails != null){
            customUserDetails.eraseCredentials();
        }
        return customUserDetails;
    }

    private CustomUserDetails loadUser(String s) throws UsernameNotFoundException, JSONException {

        Account userAccount = accountDbRepository.getAccountByUserName(s);
        if (userAccount==null){
            return null;
        }
        Collection<GrantedAuthority> grantedAuthoritySet = new HashSet<>();

        for (int i=0; i<userAccount.getRoles().size();i++)
        {
            JSONObject jsonObject = new JSONObject(userAccount.getRoles().get(i));
            String role = jsonObject.getString("role");
            gas.add(new SimpleGrantedAuthority(role));
        }
        return new CustomUserDetails(userAccount.getEmail(),userAccount.getDisplayName(),userAccount.getUserName(),userAccount.getPassword(),userAccount.getEnabled(),gas);

    }

}

CustomUserDetails.java

public class CustomUserDetails implements UserDetails {

private Collection<? extends GrantedAuthority> authorities;

private String email;

private String displayName;

private String password;

private String username;

private Boolean enabled;

private Boolean accountNonExpired;

private Boolean accountNonLocked;

private boolean credentialsNonExpired;

public CustomUserDetails(String email, String displayName, String username, String password, Boolean enabled, Collection<? extends GrantedAuthority> authorities) {
    this.email = email;
    this.displayName = displayName;
    this.enabled=enabled;
    this.username=username;
    this.password=password;
    this.accountNonExpired=true;
    this.accountNonLocked=true;
    this.credentialsNonExpired=true;
    this.authorities=authorities;
}

public CustomUserDetails(String email, String displayName, String password, String username, Boolean enabled, Boolean accountNonExpired, Boolean accountNonLocked, boolean credentialsNonExpired, Collection<? extends GrantedAuthority> authorities) {
        this.authorities = authorities;
        this.email = email;
        this.displayName = displayName;
        this.password = password;
        this.username = username;
        this.enabled = enabled;
        this.accountNonExpired = accountNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.credentialsNonExpired = credentialsNonExpired;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

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

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

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

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

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

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

    public void eraseCredentials(){
        this.password=null;
    }

}
Nubia answered 27/12, 2019 at 10:51 Comment(2)
Hi, my User class has fields like confirmPassword, createdAt, fullName. Do I have to add all those fields to my CustomUserDetails class?Mainsheet
@AbubakarIbrahim you are correct. You need to add those in CustomUserDetails which implements UserDetails interface (spring-security-core)Nubia
C
1

Don't directly wite userRole.getRole() directly if you want to send the role as ADMIN/USER instead write it as "ROLE_" + userRole.getRole(). So that you can get the error-free run of your code.

Example:

@Service("listUserDetailsService")
public class ListUserDetailsService implements UserDetailsService {

    @Transactional(readOnly = true)
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException{

        User user = userRepo.findByEmail(email);

        if (user == null) {
            throw new UsernameNotFoundException("Invalid User");

        } else {
            Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
            for (Role role : user.getRoles()){
                grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
            }


            return new org
                    .springframework
                    .security
                    .core
                    .userdetails
                    .User(user.getEmail(), user.getPassword(), grantedAuthorities);

        }

    }

    private final UserRepo userRepo;

    public ListUserDetailsService(UserRepo userRepo) {  
        this.userRepo = userRepo;

    }

}
Canner answered 23/5, 2020 at 20:28 Comment(0)
E
1

Instead of implements the interface UserDetails, it is more clean and simple by extending class org.springframework.security.core.userdetails.User.

import org.springframework.security.core.userdetails.User;

public class CustomUserDetails extends User {
    // you can add custom field here, example field 'name'
    private String name;

    public CustomUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }
    // don't forget getter-setter
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

And then, once authenticated, you can access this object anywhere in the project like this.

CustomUserDetails user = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Erickaericksen answered 6/11, 2023 at 2:48 Comment(0)
C
0

To create the user details, we can convert a list of user roles that are in list form into an array: To create this array of the string, use {roles(user.getRole().toArray(new String[0]))} instead of user.getRole(). Please review the image that is linked.

Commotion answered 1/8 at 9:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.