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