25

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.

nikiforovpizza
  • 487
  • 1
  • 7
  • 13
Jiji
  • 1,092
  • 3
  • 15
  • 36
  • what's the `UserDetailsServiceImplementation`? I believe thats the place that should link your entity to the request principal. – zapl Jun 03 '16 at 13:01
  • Ok, so tracing the issue leads to `CurrentUserDetails.create(this)`. What does that do? – zapl Jun 03 '16 at 13:08
  • @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. – Jiji Jun 03 '16 at 13:31
  • You need to return the roles via `getAuthorities()` - http://stackoverflow.com/questions/19525380/difference-between-role-and-grantedauthority-in-spring-security - spring does not look at your own `getRole` method. – zapl Jun 03 '16 at 13:39
  • Does it automatically sets it via getAuthorities()? – Jiji Jun 03 '16 at 13:44

4 Answers4

33

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.

Michal
  • 15,429
  • 10
  • 73
  • 104
DiveInto
  • 2,158
  • 3
  • 27
  • 50
  • 2
    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. – thorinkor Feb 02 '17 at 08:21
  • 1
    Should role contain ROLE_ prefix? And if the role is ROLE_ADMIN hasRole should specify 'ADMIN' or 'ROLE_ADMIN'? – Pavan Kumar Oct 23 '19 at 04:20
  • 1
    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 – Saroj Kumar Sahoo Feb 25 '20 at 07:30
  • 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. – Malik Firose Feb 04 '21 at 06:57
4

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

Morteza Jalambadani
  • 2,190
  • 6
  • 21
  • 35
Ajay Kumar
  • 2,906
  • 3
  • 23
  • 46
  • 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. – rougou Nov 20 '18 at 02:59
  • 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. – Ajay Kumar Nov 20 '18 at 05:10
  • 1
    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. – BendaThierry.com Apr 22 '22 at 13:11
1

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());
}
Morteza Jalambadani
  • 2,190
  • 6
  • 21
  • 35
1

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