Spring Security/Spring Boot - How to set ROLES for users

92,564

Solution 1

You should fill in the content of role by yourself when creating your UserDetails:

public class SecurityUser implements UserDetails{
    String ROLE_PREFIX = "ROLE_";

    String userName;
    String password;
    String role;

    public SecurityUser(String username, String password, String role){
        this.userName = username;
        this.password = password;
        this.role = role;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();

        list.add(new SimpleGrantedAuthority(ROLE_PREFIX + role));

        return list;
    }

Basically, what you need to do is override method: getAuthorities, and fill in the content of your role field into the GrantedAuthority list.

Solution 2

What Divelnto, zapl and thorinkor said is right. But the question should be about "Role" and NOT "Roles". OR, if you are having users and roles into one table, its a bad design. You might want to take a relook at your design approach. You should have a separate role entity. And in your UserService you can do something like:

AppUser user = userRepository.findByUsername(username);

Set<GrantedAuthority> grantedAuthorities = new HashSet<>(); // use list if you wish
for (AppRole role : user.getRoles()) {
    grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(
        user.getUsername(),
        user.getPassword(),
        grantedAuthorities
);

Samples: sample1 sample2 sample3

In DB, you can store role name as - (e.g.) ADMIN/EDITOR/VIEWER in the database or store roles as ROLE_ADMIN/ROLE_... then you might wanna use hasRole/hasAuthoriy. Hope it helps.

For reference, take a look at here:

Spring Security Related 1

Spring Security Related 2

Solution 3

For adding Roles you need to have a table containing username and its corresponding role.
Suppose a user has two roles namely, ADMIN and USER

One User can have multiple roles.

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    final List<SimpleGrantedAuthority> authorities = new LinkedList<>();
    if (enabled) {
        if (this.getUser().isAdmin()) {
            authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        }
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
    }
        return authorities;
}

This can be called as,

private UsernamePasswordAuthenticationToken getAuthentication(
final String token, final HttpServletRequest req,
final HttpServletResponse res){
    return new UsernamePasswordAuthenticationToken(userAccount, null,
    userAccount.getAuthorities());
}

Solution 4

You can also used this way to load ROLEs or Authorities using User.builder object. This example is receiving token from http request and it gets the list o ROLES inside TOKEN. You can have a simple list of strings containing the roles you need and just loaded to the UserDetail implementation:

private UserDetails userDetails(final Claims claims) {
    
    UserDetails userDetails = User.builder()
    .username(resolveUserName(claims))
    .password(resolveUserPassword())
    .roles(userRoles(claims))
    .build();
    
    
    return userDetails;
}

where userRoles(claims) is just returning an array of strings with all Roles you need.

I hope it helps

Share:
92,564

Related videos on Youtube

Jiji
Author by

Jiji

Here to ask until I can answer.

Updated on July 22, 2022

Comments

  • Jiji
    Jiji almost 2 years

    When I logged in using security, I cannot use the request.isUserInRole() method. I think the roles of the users was not set.

    This is my Security Configuration:

    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled=true)
    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    public class SecurityConfig extends WebSecurityConfigurerAdapter  {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private UserDetailsServiceImplementation userDetailsService;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/signup").permitAll()
                .antMatchers("/").permitAll()
                //.antMatchers("/first").hasAuthority("Service_Center")
                .antMatchers("/login").permitAll()
                .anyRequest().fullyAuthenticated()
        .and().formLogin()
                .loginPage("/login")
                .usernameParameter("email")
                .passwordParameter("password")
                .defaultSuccessUrl("/default")
                .failureUrl("/login?error").permitAll()
        .and().logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login?logout")
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true).permitAll();
    }
    
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService);
    
    }
    
    }
    

    This is my User entity:

     @Entity
     @Table(name="user")
     public class User  implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="user_id")
    private Long userID;
    
    @Column(name="email_address", nullable = false, unique = true)
    private String emailAddress;
    
    @Column(name="password")
    private String password;
    
    @Column(name = "role", nullable = false)
    @Enumerated(EnumType.STRING)
    private Role role;
    
    public User() {
        super();
    }
    
    public User(String emailAddress, String password) {
        this.emailAddress = emailAddress;
        this.password = password;
    }
    
    public Long getUserID() {
        return userID;
    }
    
    public void setUserID(Long userID) {
        this.userID = userID;
    }
    
    public String getEmailAddress() {
        return emailAddress;
    }
    
    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
    
    public String getPassword() {
        return password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    public Role getRole() {
        return role;
    }
    
    public void setRole(Role role) {
        this.role = role;
    }
    
    @Override
    public String toString() {
        return "User [userID=" + userID + ", emailAddress=" + emailAddress
                + ", password=" + password + ", role=" + role + "]";
    }
    
    public UserDetails toCurrentUserDetails() {
        return CurrentUserDetails.create(this);
    }
    }
    

    This is my enum Role:

    public enum Role {
    
    Fleet_Company, Service_Center, Admin
    
    }
    

    This is my UserDetailsServiceImplementation:

    @Component
    public class UserDetailsServiceImplementation implements UserDetailsService    {
    
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        if ( username == null || username.isEmpty() ){
            throw new UsernameNotFoundException("username is empty");
        }
    
        User foundUser = userRepository.findByEmailAddress(username);
        if( foundUser != null ){
            System.out.println("FOUND");
            return foundUser.toCurrentUserDetails();
    
        }
        throw new UsernameNotFoundException( username + "is not found");
    }
    }
    

    This is the class that implements UserDetails:

    public class CurrentUserDetails implements UserDetails {
    private Long userID;
    private String emailAddress;
    private String password;
    private Role role;
    
    
    public CurrentUserDetails(Long userID, String emailAddress, String password, Role role) {
        super();
        this.userID = userID;
        this.emailAddress = emailAddress;
        this.password = password;
        this.role = role;
    }
    
    
      /*    public static UserDetails create(Users entity) {
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        for(Authorities auth: entity.getAuthorities()){
            authorities.add(new SimpleGrantedAuthority(auth.getId().getAuthority()));
        }
        return new MyUserDetail(entity.getUserId(), entity.getLoginId(), entity.getPassword(), entity.getDisplayName(), authorities);
    }*/
    
    
    
    public Long getUserID(){
        return this.userID;
    }
    
    
    public Role getRole(){
        return this.role;
    }
    
    
    
    
    @Override
    public String getPassword() {
        return this.password;
    }
    
    
    public String getEmailAddress() {
        return this.emailAddress;
    }
    
    
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    
    
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    
    
    @Override
    public boolean isEnabled() {
        return true;
    }
    
    public static UserDetails create(User entity) {
        System.out.println(entity.getUserID()+ entity.getEmailAddress()+ entity.getPassword()+ entity.getRole());
        return new CurrentUserDetails(entity.getUserID(), entity.getEmailAddress(), entity.getPassword(), entity.getRole());
    }
    
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // TODO Auto-generated method stub
        return null;
    }
    
    @Override
    public String getUsername() {
        // TODO Auto-generated method stub
        return null;
    }
    }
    

    So basically, we can see that I only have one table on my MySQL database, it has four columns and one of them is 'role'.

    But like what I said, when I use request.isUserInRole("Service_Center"), it returns FALSE. And .antMatchers("/first").hasAuthority("Service_Center") doesn't work either.

    • zapl
      zapl almost 8 years
      what's the UserDetailsServiceImplementation? I believe thats the place that should link your entity to the request principal.
    • zapl
      zapl almost 8 years
      Ok, so tracing the issue leads to CurrentUserDetails.create(this). What does that do?
    • Jiji
      Jiji almost 8 years
      @zapl that was the class that implements UserDetails. I edited my post. You can see it now. That's basically all of my Spring Security.
    • zapl
      zapl almost 8 years
      You need to return the roles via getAuthorities() - stackoverflow.com/questions/19525380/… - spring does not look at your own getRole method.
    • Jiji
      Jiji almost 8 years
      Does it automatically sets it via getAuthorities()?
  • thorinkor
    thorinkor about 7 years
    adding new Role on getAuthorities method? Doesn't look like it is a good idea... You should use SecurityContextHolder and do it in separate service.
  • rougou
    rougou over 5 years
    You said it is bad design to have users and roles in one entity, but your sample code seems to do exactly that... I think it is simpler to have them together if your DB allows that.
  • Ajay Kumar
    Ajay Kumar over 5 years
    Don't get me wrong bud.! Do you see a 'column' - "role" in any of the User entities in the samples? Moreover if a User is supposed to have just one role, why you need a role in the first place? Its a wage idea. Instead of using "role" (not "roles") you can put a flag as true/false with if-else condition that will solve the purpose In-My-Humble-Opinion. Thanks.
  • Pavan Kumar
    Pavan Kumar over 4 years
    Should role contain ROLE_ prefix? And if the role is ROLE_ADMIN hasRole should specify 'ADMIN' or 'ROLE_ADMIN'?
  • Saroj Kumar Sahoo
    Saroj Kumar Sahoo about 4 years
    why do we need to add ROLE_PREFIX any specific reason . I have developed one application where I am returning what ever coming from database just split it by comma and I am putting into a variable which has List of GrantedAuthority . But in database we must have to specify ROLE_PREFIX .I want to understand why
  • Malik Firose
    Malik Firose about 3 years
    Spring uses it for some internal purposes. Inside the User.UserBuilder roles as you can see spring creates the SimpleGrantedAuthority with the ROLE prefix like authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); As long as if you use the builder you don't need to give the prefix.
  • BendaThierry.com
    BendaThierry.com about 2 years
    It depends about your application needs and functionnal point of view. Let's explain a bit about that. Some applications handle one role for one user, and roles are following hierachical inheritance between them. Some applications handle multiples roles for one same user and multiple authorities for one same user. So none of you has wrong and none of you are right. It depends.