Describe the bug I had a ticket to upgrade one of the springboot application to spring boot 3 version after upgrade everything. I also modified the WebSecurityConfig in order that specification of spring security explained. when run the test cases for all those scenario that test case expect 200 instead I am getting 403 or 401. I started debugging and realized the attemptAuthentication that has been overriden a class from AbstractAuthenticationProcessingFilter never intercepted when mockmvc is performing the api.
To Reproduce after running mvn test all those test cases that expected an authenticated response return 401
Expected behavior After modifying WebSecurityConfig expect the test units can be tested and assert with expected the http response were getting failure. After debugging I realized the attemptAuthenticaion from AuthenticationManager class never being called when mockmvc performs an api that required to be authenticated.
Sample
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { MvcRequestMatcher[] endPointsToIgnoreCsrf = new MvcRequestMatcher[]{ ignoreCsrfOnPost("/api/v1/service"), ignoreCsrfOnPost("/api/v1/service/webOrderId/{webOrderId}/book") };
// @formatter:off
var httpSecurity = http
.cors(configurer -> configurer.configurationSource(corsConfigurationSource()))
.csrf(customizer -> customizer.ignoringRequestMatchers(endPointsToIgnoreCsrf))
.addFilterBefore(RequestLoggingFilter.defaultConfiguration(), AnonymousAuthenticationFilter.class) // Logging filter
.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
;
// @formatter:on
if (true) {
// @formatter:off
httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.addFilterAfter(requestTokenAuthenticationFilter(httpSecurity), RequestLoggingFilter.class) // Custom authentication filter
.addFilterAfter(webOrderRequestAuthorizationFilter(), RequestTokenAuthenticationFilter.class) // Custom authorization filter
// Permissions (START)
.authorizeHttpRequests(rmr -> rmr
.requestMatchers(antMatcher(HttpMethod.GET, "/api/v1/service/webOrderId/{webOrderId}")).hasAuthority(Permission.READ_APPOINTMENT.name())
.requestMatchers(antMatcher(HttpMethod.GET, "/api/v1/service/webOrderId/{webOrderId}/available-slots")).hasAuthority(Permission.BOOK_APPOINTMENT.name())
.requestMatchers(antMatcher(HttpMethod.POST, "/api/v1/service/webOrderId/{webOrderId}/book")).hasAuthority(Permission.BOOK_APPOINTMENT.name())
// Permissions (END)
.requestMatchers(PROTECTED_URLS).authenticated());
// All protected URLs need authentication
// @formatter:on
}
return httpSecurity.getOrBuild();
}
//////////////////////////////// @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder.authenticationProvider(tokenAuthenticationProvider); return authenticationManagerBuilder.build(); } ////////////////////////////////// and here is code of public class RequestTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public RequestTokenAuthenticationFilter(RequestMatcher requestMatcher, AuthenticationManager authenticationManager) {
super(requestMatcher);
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
var authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
var guestEmailHeader = request.getHeader("X-Guest-Email");
var guestAuthCookie = WebUtils.getCookie(request, "...");
var loggedInUserAuthCookie = WebUtils.getCookie(request, "...");
String principal = null;
String token = null;
AuthenticationChannel authenticationChannel = null;
if (!ObjectUtils.isEmpty(authorizationHeader)) {
...
}
// Create a pre-authenticated authentication token object
var authToken = AuthenticationResultToken.createPreauthenticatedResultToken(authenticationChannel, principal, token);
// Pass it down to the authentication manager
// This internally should call the authentication provider configured
return getAuthenticationManager().authenticate(authToken);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
// Once successfully authenticated, continue down the filter chain
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
Comment From: marcusdacoregio
Hi, @shhb. Have you tried adding logging.level.org.springframework.security=TRACE to your application.properties and investigate the console logs to figure out what is happening?
I also recommend reading the migration guide since a lot change between versions 5 and 6, specially the session management section, which I need you will need to change the implementation of RequestTokenAuthenticationFilter#successfulAuthentication.
If you tried what I mentioned above and had no success so far, please provide a minimal, reproducible sample so we can make sure we won't miss anything on our side.
Comment From: shhb
Hey @marcusdacoregio thanks for quick feedback. I turned on TRACE on security package but didn't find anything majorly wrong. The main problem here attemptAuthentication from RequestTokenAuthenticationFilter that implemented AbstractAuthenticationProcessingFilter not being called which causing getting 401 on mockmvc performing those APIs and previously with prior version without any issue it was being called. either attemptAuthentication and successfulAuthentication none of them have been called during mockmvc call/perform I see the constructor being called due to annotated as Bean in WebSecurityConfig I already pasted again those classes fully or partially, if you tell me what else you need I can drop it here(for sure not possible to do for all the classes just these ones which are involved in this issue)
Comment From: marcusdacoregio
Hi, @shhb. As per my last comment, in order to make sure we do not miss anything, we need a minimal, reproducible sample that allow us to debug exactly what is happening in your scenario.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.