I am having some problems with the new Spring Security configuration. I am using the spring boot version 3.1.4 and the spring security version 6.1.4.
In the config file I define that some pages should be accessible without authentication. After that I add my JwtAuthFilter to the chain. However it is called allways. Even for the endpoints that should be allowed. The error page, which is called for 403 errors is for example allowed to be accessed without authentication. Unfortunately it is also tried to authenticated by the jwtAuthfilter, which leads to another 403 error, which leads to a authentication loop. How can i fix that?
This is my config and my filter.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtAuthFilter jwtAuthFilter;
private final UserProfileRepository userProfileRepository;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(auth -> {
auth.requestMatchers("/").permitAll();
auth.requestMatchers("/login").permitAll();
auth.requestMatchers("/register").permitAll();
auth.requestMatchers("/error/**").permitAll();
})
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider())
.addFilterAfter(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public UserDetailsService userDetailsService() {
return username -> {
RegisteredUser registeredUser = userProfileRepository.findByMail(Email.of(username));
if (registeredUser == null) {
throw new UsernameNotFoundException("User not found");
}
return registeredUser;
};
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService());
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
@Bean
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
return authentication -> {
UserDetails user = userDetailsService.loadUserByUsername(authentication.getName());
if (passwordEncoder.matches(authentication.getCredentials().toString(), user.getPassword())) {
return new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
user.getAuthorities());
}
throw new BadCredentialsException("Bad Password");
};
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
} //TODO read from properties
}
This is my Filter
@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserProfileRepository userProfileRepository;
@Override
protected void doFilterInternal(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain) throws ServletException, IOException {
final Cookie[] cookies = request.getCookies();
if (cookies == null || cookies.length == 0) {
filterChain.doFilter(request, response);
return;
}
Optional<Cookie> authorizationCookie = Arrays.stream(cookies)
.filter(cookie -> cookie.getName().equals("Authorization"))
.findFirst();
if (authorizationCookie.isEmpty()) {
filterChain.doFilter(request, response);
return;
}
String token = authorizationCookie.get().getValue();
String mail = jwtService.extractMail(token);
if (mail == null) {
filterChain.doFilter(request, response);
return;
}
SecurityContext context = SecurityContextHolder.getContext();
if (context.getAuthentication() != null) {
filterChain.doFilter(request, response);
return;
}
RegisteredUser user = userProfileRepository.findByMail(Email.of(mail));
if (user == null) {
filterChain.doFilter(request, response);
return;
}
if (jwtService.isTokenValid(token, user)) {
var authenticationToken = new UsernamePasswordAuthenticationToken(
user,
null,
user.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
context.setAuthentication(authenticationToken);
}
filterChain.doFilter(request, response);
}
}