How to change the role of a user in CustomUserDetails?

107 Views Asked by At

I wrote my implementation of UserDetails in order to change the login and user roles. With the login, everything turned out to be quite simple, but I had problems with the roles. I don't understand what I need to do in order to change the default CUSTOMER role to another SELLER role

@ToString
public class CustomUserDetails implements UserDetails {
    @ToString.Exclude
    private Account account;

    private final long serialVersionUID = 1L;

    public CustomUserDetails(Account account) {
        this.account = account;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_" + this.account.getRole().getAuthority()));

        return authorities;
    }

    public void setRole(Role role) {
        this.account.setRole(role);
    }

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

    @Override
    public String getUsername() {
        return this.account.getLogin();
    }

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

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

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

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

    @Override
    public boolean isEnabled() {
        return true;
    }
}
3

There are 3 best solutions below

0
Mark On BEST ANSWER

Perhaps I should have been more specific about my question. But. I found a solution to this problem for myself. Maybe my method will help someone.

My CustomUserDetailsService:

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

    @Autowired
    private AccountRepository accountRepository;

    @Override
    public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
        Account account = accountRepository.findByLogin(login);
        if (account == null) {
            throw new UsernameNotFoundException(login);
        }

        return new CustomUserDetails(account, mapRolesToAuthority(account.getRole()));
    }

    private Collection<? extends GrantedAuthority> mapRolesToAuthority(Role role) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getAuthority()));
        authorities.add(new SimpleGrantedAuthority(role.getAuthority()));

        return authorities;
    }
}

My CustomUserDetails:

@ToString
public class CustomUserDetails implements UserDetails {
    @ToString.Exclude
    private Account account;
    private Collection<? extends GrantedAuthority> authorities;

    private final long serialVersionUID = 1L;

    public CustomUserDetails(Account account, Collection<? extends GrantedAuthority> authorities) {
        this.account = account;
        this.authorities = authorities;
    }

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

    public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
        this.authorities = authorities;
    }

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

    @Override
    public String getUsername() {
        return this.account.getLogin();
    }

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

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

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

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

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

Method in the controller that changes data in the user session (needs refactoring):

    @PostMapping("/trade")
    public String updateAccountRole(Principal principal) {
        Account account = accountRepository.findByLogin(principal.getName());
        account.setRole(Role.SELLER);

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_" + account.getRole().getAuthority()));
        authorities.add(new SimpleGrantedAuthority(account.getRole().getAuthority()));
        userDetails.setAuthorities(authorities);

        System.out.println("BEFORE -> " + SecurityContextHolder.getContext().getAuthentication());
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
                SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
                SecurityContextHolder.getContext().getAuthentication().getCredentials(),
                userDetails.getAuthorities()
        );
        token.setDetails(SecurityContextHolder.getContext().getAuthentication().getDetails());
        SecurityContextHolder.getContext().setAuthentication(token);
        System.out.println("AFTER -> " + SecurityContextHolder.getContext().getAuthentication());

        accountRepository.save(account);

        return "redirect:/";
    }

Thus, I managed to achieve a dynamic change of roles for the user. If you have any suggestions on how my method can be improved, I'd love to hear it.

0
DmitriKonnov On

In case, u ain't got no service yet that'd be store your CustomUserDetails entities into DB:

Your CustomUserDetails.class must have annotation @Entity , as it's supposed to be stored as table in relational db. Thus, it has to have an id field annotated with @Id . Next:

public interface CustomUserRepo extends 
CrudRepository<CustomUserDetails, ID_TYPE> {

CustomUserDetails loadUserByArgThatYouWant (String ARG_THAT_YOU_WANT);

@Transactional
@Modifying
@Query("update CustomUserDetails c set c.role = ?1 where c.id = ?2")
void updatRoleById(String role, ID_TYPE);
}

public class UserService implements UserDetailsService {
@Autowired
private final CustomUserRepo repo;
@Override
CustomUserDetails loadUserByUsername (String arg) {
return repo.loadUserByArgThatYouWant(arg);
}
// down below method that you need

public void updateRoleById (String role, ID_TYPE id){
repo.updateRoleById(role,id);}

}
0
Gun Slinger On

if you want to assign a role then you can have a column in your database roles which you can give at the time of submitting your data and you can have a method in your controller to update it also

**Entity **

@Entity
@Table(name="users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String roles;

    //getter and setters