October 22, 2024
Chicago 12, Melborne City, USA
java

Role-based authorization in SecurityFilterChain not working


I’m trying to secure endpoints based on Roles. There are 3 roles: ADMIN, ESTUDIANTE (student) and EMPRESA (company) and a user can only have 1. I also don’t store the roles in a database. And I was trying to make it so that only ADMINs can add instances of CarreraEntity, but when I try to secure those endpoints on the SecurityFilterChain I get a 403 Forbidden or I can access to that endpoint despite the role or even not being authenticated.

This is my SecurityConfig class.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final AuthenticationProvider authProvider;
    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    public SecurityConfig(AuthenticationProvider authProvider, JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.authProvider = authProvider;
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(authRequest ->
                        authRequest
                                .requestMatchers("/api/v1/auth/**").permitAll()
                                .requestMatchers("/api/v1/auth/contrasenia").hasAnyRole(
                                        Role.ADMIN.name(), Role.EMPRESA.name(), Role.ESTUDIANTE.name()
                                )
                                .requestMatchers(HttpMethod.GET, "/api/v1/carreras/**").permitAll()
                                .requestMatchers(HttpMethod.POST, "/api/v1/carreras/**").hasRole(Role.ADMIN.name())
                                .anyRequest().authenticated()
                )
                .sessionManagement(sessionManager ->
                        sessionManager
                                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authenticationProvider(authProvider)
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}

These are the endpoints which are causing problems:

@GetMapping
public ResponseEntity<Set<CarreraDTO>> getCarreras() {
    return ResponseEntity.ok(carreraService.getCarreras());
}

// TODO: return 201
@PostMapping(consumes = "application/json")
public ResponseEntity<CarreraDTO> postCarrera(@RequestBody CarreraRequest request) {
    return ResponseEntity.ok(carreraService.postCarrera(request));
}

This is how I assign the role in the claims:

public String getToken(UserDetails user) {
    HashMap<String, Object> claims = new HashMap<>();

    String role = user.getAuthorities()
                .stream()
                .findFirst()
                .map(GrantedAuthority::getAuthority)
                .orElseThrow();
    claims.put("role", role);

    return getToken(claims, user);
}

private String getToken(HashMap<String,Object> map, UserDetails user) {
    return Jwts.builder()
                .claims(map)
                .subject(user.getUsername())
                .issuedAt(new Date(System.currentTimeMillis()))
                .expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24))
                .signWith(this.getKey(), Jwts.SIG.HS256)
                .compact();
}

And this is my JwtAuthenticationFilter class:

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtService jwtService;
    private final UserDetailsService userDetailsService;

    @Autowired
    public JwtAuthenticationFilter(JwtService jwtService, @Qualifier("userDetailsService") UserDetailsService userDetailsService) {
        this.jwtService = jwtService;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        final String token = this.getTokenFromRequest(request);
        final String username;

        if (token == null) {
            filterChain.doFilter(request, response);
            return;
        }

        username = jwtService.getUsernameFromToken(token);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            if (jwtService.isTokenValid(token, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails,
                        null,
                        userDetails.getAuthorities()
                );

                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        filterChain.doFilter(request, response);
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);

        return jwtService.getTokenFromAuthorizationHeader(authHeader).orElse(null);
    }
}

I tried to use MethodSecurity instead (@PermitAll, @PreAuthorize). I also tried to use hasAuthority(). But none of these workarounds solved it. I don’t know what else to do.



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video