arvind.mohan
arvind.mohan

Reputation: 914

Springboot Security hasRole not working

I’m unable to use hasRole method in @PreAuthorize annotation. Also request.isUserInRole(“ADMIN”) gives false. What am I missing? Although .hasAuthority(“ADMIN”) works fine.

I am assigning authorities to the users from a database.

Upvotes: 18

Views: 20408

Answers (4)

Shehan Simen
Shehan Simen

Reputation: 1306

You can use either hasRole() or hasAuthority(). The difference is that, you have to user ROLE_ for hasAuthority() method.

So for the ROLE_ADMIN:

@PreAuthorize("hasRole('ADMIN')") == @PreAuthorize("hasAuthority('ROLE_ADMIN')")

Upvotes: 1

ideapad5
ideapad5

Reputation: 1

Please double check at SecurityConfig file

@EnableMethodSecurity for Spring Boot 3.xx and after
@EnableGlobalMethodSecurity(prePostEnabled = true) for Spring Boot 2.xx

Upvotes: 0

dur
dur

Reputation: 17009

You have to name your authority with prefix ROLE_ to use isUserInRole, see Spring Security Reference:

The HttpServletRequest.isUserInRole(String) will determine if SecurityContextHolder.getContext().getAuthentication().getAuthorities() contains a GrantedAuthority with the role passed into isUserInRole(String). Typically users should not pass in the "ROLE_" prefix into this method since it is added automatically. For example, if you want to determine if the current user has the authority "ROLE_ADMIN", you could use the following:

boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");

Same for hasRole (also hasAnyRole), see Spring Security Reference:

Returns true if the current principal has the specified role. By default if the supplied role does not start with 'ROLE_' it will be added. This can be customized by modifying the defaultRolePrefix on DefaultWebSecurityExpressionHandler.

See also Spring Security Reference:

46.3.3 What does "ROLE_" mean and why do I need it on my role names?

Spring Security has a voter-based architecture which means that an access decision is made by a series of AccessDecisionVoters. The voters act on the "configuration attributes" which are specified for a secured resource (such as a method invocation). With this approach, not all attributes may be relevant to all voters and a voter needs to know when it should ignore an attribute (abstain) and when it should vote to grant or deny access based on the attribute value. The most common voter is the RoleVoter which by default votes whenever it finds an attribute with the "ROLE_" prefix. It makes a simple comparison of the attribute (such as "ROLE_USER") with the names of the authorities which the current user has been assigned. If it finds a match (they have an authority called "ROLE_USER"), it votes to grant access, otherwise it votes to deny access.

Upvotes: 34

Omar Ajmi
Omar Ajmi

Reputation: 190

I had to improvise a little, maybe there is other ways simpler then mine, but at the time I worked on this I had no other choice but to improvise a bit, after a thorough research came up with this solution. Spring Security has an interface called AccessDecisionManager, you will need to implement it.

@Component
public class RolesAccessDecisionManager implements AccessDecisionManager {
    private final static String AUTHENTICATED = "authenticated";
    private final static String PERMIT_ALL = "permitAll";

    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        collection.forEach(configAttribute -> {
            if (!this.supports(configAttribute))
                throw new AccessDeniedException("ACCESS DENIED");
        });
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            String rolesAsString = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(","));
            if (configAttribute.toString().contains(rolesAsString))
                return true;
            else
                return (configAttribute.toString().contains(PERMIT_ALL) || configAttribute.toString().contains(AUTHENTICATED));
        }
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

Now to support this custom access-decision-manager with your security config do this in the security configuration:

@Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
// other configs
    .accessDecisionManager(this.accessDecisionManager)

accessDecisionManager is the autowired bean of the AccessDecisionManager implementation you've created.

Upvotes: 1

Related Questions