Reputation: 307
I wrote a JWT-based authentication flow for my Spring boot application. It adds two Filters to the chain: a JwtAuthenticationFilter and a JwtAuthorizationFilter. Adding them to the chain happens in my SecurityConfig class:
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers(H2CONSOLE_LOCATION).permitAll()
.antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()
.antMatchers(HttpMethod.POST, LOGIN_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager(), tokenService))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), tokenService))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
This is the setup for my Integration test (written in groovy using spock):
@SpringBootTest(classes = CarshareUsersApplication.class)
@TestPropertySource(locations = "classpath:application-local.properties")
@ActiveProfiles("local")
@WebAppConfiguration
@ExtendWith(SpringExtension.class)
class UserRestControllerIT extends Specification {
@Autowired
ObjectMapper mapper
private MockMvc mvc
@Autowired
private WebApplicationContext context
@Autowired
private Filter springSecurityFilterChain;
def setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.build()
}
However, when I post a request using my mockMvc, the filters aren't used. I put breakpoints in the filters and in the SecurityConfig, but only the ones in the SecurityConfig get hit.
When inspecting my mockMVC I saw that my springSecurityFilterChain actually holds 3 filterChains, but they all match any request. Only one (the second one) holds my custom filters. See the screenshot below for the contents:
Looking at the debug log confirms my suspicion:
Request received for POST '/login':
org.springframework.mock.web.MockHttpServletRequest@10ea1754
servletPath:
pathInfo:/login
headers:
Content-Type: application/json
Content-Length: 170
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
I don't get where the first and third filterchain come from, and why they all match everything. Can anyone give me some pointers on how to work with only one chain that holds my custom filters?
Upvotes: 1
Views: 712
Reputation: 307
I found where the other two chains came from: I had one TestSecurityConfiguration and another SecurittyConfiguration from when I was still using basic auth that were both being loaded. That together with the SecurityConfig from my post makes 3 WebSecurityConfigurerAdapters, resulting in 3 chains.
I found this by diving into the Spring Security source code, looking for where securityChains are added to the final configuration. This led me to the WebSecurity class and the securityFilterChainBuilders property. A quick "find usages" led me to the addSecurityFilterChainBuilder method. A breakpoint there and then looking through the call stack led me to my 3 SecurityConfigurerAdapters.
An interesting deep dive to be sure :)
Upvotes: 2
Reputation: 1385
@Autowired
private WebApplicationContext context
@Autowired
private Filter springSecurityFilterChain;
def setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.build()
}
To my understanding the context would be already initialized with a filter chain and you add one more. That would explain at least one superfluous. No idea why there is another one.
Could you compare object id of springSecurityFilterChain
with the list of filter chains in your debugger?
Upvotes: 0