I have an issue in my Spring Boot 3.0.2 project that uses Spring Security 6.0.2. I have created the following JwtAuthenticationFilter
class for authorizing JWTs:
@Log4j2
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserDetailsService userDetailsService;
private final ObjectMapper objectMapper;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws IOException {
try {
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
String errorPayload = objectMapper.writeValueAsString(Map.of(
"type", "about:blank",
"title", "Bad Authorization",
"status", HttpServletResponse.SC_UNAUTHORIZED,
"detail", "The Authorization header should contain a Bearer token value."
));
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/problem+json");
response.getWriter().write(errorPayload);
return;
}
final String jwt = authHeader.substring(7);
final String userEmail = jwtService.extractUsername(jwt);
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail);
if (jwtService.isTokenValid(jwt, userDetails)) {
var authToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
String errorPayload = objectMapper.writeValueAsString(Map.of(
"type", "about:blank",
"title", "SF1",
"status", HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"detail", "An error occurred while processing the request."
));
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.setContentType("application/problem+json");
response.getWriter().write(errorPayload);
}
}
}
This is my Spring Security configuration class:
@Configuration
@EnableWebSecurity(debug = true)
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(11);
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults())
.csrf(CsrfConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/auth/**")
.permitAll()
.requestMatchers(HttpMethod.OPTIONS, "/**")
.permitAll()
.anyRequest()
.authenticated()
)
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public CorsFilter corsFilter() {
var source = new UrlBasedCorsConfigurationSource();
var config = new CorsConfiguration();
config.applyPermitDefaultValues();
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
config.setAllowedOrigins(List.of("http://localhost:4200"));
config.setAllowCredentials(true);
config.addAllowedHeader("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
When I comment out the line .addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
, the authorization rules work correctly and I can obtain the JWT. However when I add this filter, it gets triggered for every request and I can no longer call the endpoint for obtaining the JWT, because its protected.