Home > other >  Spring Security @RolesAllowed not working
Spring Security @RolesAllowed not working

Time:10-19

Here is my User Details Object

2022-10-19 07:35:58.143  INFO 24580 --- [nio-8081-exec-6] c.s.jwt.api.filter.JwtFilter             : User Details: org.springframework.security.core.userdetails.User [Username=502553205, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[Authority(id=1, authority=Engine Line Tracker, description=Engine Line Tracker), Authority(id=2, authority=Pin a Card, description=Pin a Card), Authority(id=5, authority=Project Plan, description=Project Plan), Authority(id=4, authority=Scorecard, description=Scorecard), Authority(id=6, authority=Search, description=Search), Authority(id=3, authority=Upload/Download/Edit, description=Upload/Download/Edit)]]

My Endpoint is

    @GetMapping
    @RolesAllowed({"Scorecard"})
    public List<User> list(){
        return userRepository.findAll();
    }

This runs when I remove @RolesAllowed but not taking roles from my user object. Am I doing sth wrong in placing roles?

Yes its enabled

package com.springimplant.jwt.api.config;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.springimplant.jwt.api.filter.JwtFilter;
import com.springimplant.jwt.api.service.CustomUserDetailService;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled = false,
        securedEnabled = false,
        jsr250Enabled = true
        )
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private CustomUserDetailService userDetailService;
    
    @Autowired
    private JwtFilter filter;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
         return new BCryptPasswordEncoder();
    }
    
    @Bean(name= BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests().antMatchers("/authenticate").permitAll()
            .anyRequest().authenticated().and()
            .exceptionHandling().authenticationEntryPoint(((request, response, authException) -> {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                authException.getMessage();
            } )).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(filter,UsernamePasswordAuthenticationFilter.class);
    }
}

Also my Authority class is as follows

package com.springimplant.jwt.api.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "authorties")
public class Authority implements GrantedAuthority{

//  private static final long serialVersionUID = 1L;
    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    @Id
    @SequenceGenerator(name = "authorties_seq", sequenceName = "authorties_seq")
    @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "authorties_seq")
    private Long id;
    
    @Column(name="authority")
    private String authority;
    
    @Column(name="description")
    private String description;
    
    @Override
    public String getAuthority() {
        return authority;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof Authority) {
            return this.authority.equals(((Authority) obj).authority);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.authority.hashCode();
    }

    @Override
    public String toString() {
        return this.authority;
    }
    
    
}

CodePudding user response:

First of all, I believe it's a good practice to make role names short without spaces, slashes, and underscored. Your list may look like this: SCORECARD, PROJECT_PLAN, UPLOAD_DOWNLOAD_EDIT.

To use @RolesAllowed you have to make sure you enabled jsr250 in @EnableGlobalMethodSecurity:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {}

or for Spring 5.6 and later you can replace @EnableGlobalMethodSecurity annotation with @EnableMethodSecurity

This allows you to use @Secured, @PreAuthorized, @PostAuthorized and @RolesAllowed.

More info about it is in the official docs

===== UPD =====

Now, when you provided more details I can guess that you missed one important thing: role != authority. By default roles in Spring have ROLE_ prefix. If you didn't replace it anywhere it just tries to compare ROLE_Scorecard to Scorecard.

To remove that prefix you can do this in your config:

@Bean
fun grantedAuthorityDefaults(): GrantedAuthorityDefaults? {
    return GrantedAuthorityDefaults("") // Remove the ROLE_ prefix
}

=== UPD2 ===

Ha, one more good point appeared from Ken Chan while I were writing the update. If you're going to use an authorities approach than Ken Chan's hasAuthority() fits even better.

CodePudding user response:

One gotcha is that @RolesAllowed will expect the user has the authority that start with the prefix ROLE_ by default. So @RolesAllowed({"Scorecard"}) will expect the user has the authority ROLE_Scorecard. But now since the user only has the authority Scorecard , it does not match ROLE_Scorecard and hence it is access denied.

The easy fix is change to use @PreAuthorize :

    @GetMapping
    @PreAuthorize("hasAuthority('Scorecard')")
    public List<User> list(){
        return userRepository.findAll();
    }

It will directly check if the user has the authority Scorecard without any prefix behavior.

  • Related