javaspringsecurityprincipal

How to change the role of a user in CustomUserDetails?


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;
    }
}

Solution

  • 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.