Search code examples
javaspringauthenticationjwtuser-roles

Spring Security Role-Based Authorization Issue: 403 Forbidden Error


Problem:

I am trying to create a Spring-based web server with role-based authentication, but I consistently receive a 403 Forbidden error. I have implemented a custom UserDetails class, and I suspect there might be an issue with my configuration.

Code:

Custom UserDetails:

public class CustomUserDetails implements UserDetails {
    private static final long serialVersionUID = 1L;
    private final User user;

    public CustomUserDetails(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles().stream().map(r -> new SimpleGrantedAuthority("ROLE_" + r.getName())).toList();
    }

    // ... other UserDetails methods
}

SecurityFilterChain implementation:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
        .csrf(csrf -> csrf.disable())
        .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(requests -> requests
            .requestMatchers("/api/**").permitAll()
            .requestMatchers("/secret/**").hasAuthority("USER")
            .anyRequest().authenticated())
        .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
        .addFilterBefore(authorizeFilter, UsernamePasswordAuthenticationFilter.class)
        .build();
}

I've implemented a custom UserDetails class and configured Spring Security for role-based authentication. However, I keep encountering a 403 Forbidden error, even though I believe the roles are correctly assigned. I've tried using both hasRole and hasAuthority, but the issue persists. What am I missing in my configuration?

Any insights or suggestions would be greatly appreciated. Thank you!


Solution

  • For resource servers with JWTs, authorities are set by the authentication converter.

    The default authentication converter is JwtAuthenticarionConverter, which delegates authorities convertion to a configurable authorities converter (the default uses the entries in scope claim adding SCOPE_ prefix).

    You can provide a JwtAuthenticationConverter configured with another authorities converter (one using another claim as source for authorities), or switch to a completely different Converter<Jwt,? extends AbstractAuthenticationToken> by using http.oauth2ResourceServer(oauth2-> oauth2.jwt(Jwt -> jwt.jwtAuthenticationConverter(...))

    You might also consider this additional starter I maintain which uses an authorities converter configurable with just application properties (unless you provide your own authorities or authentication converter in your conf)