Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebMcvTest no longer auto-configures Spring Security when Using SecurityFilterChain Instead of WebSecurityConfigurerAdapter #31162

Closed
adase11 opened this issue May 25, 2022 · 12 comments
Labels
type: wiki-documentation A documentation update required on the wiki

Comments

@adase11
Copy link

adase11 commented May 25, 2022

Copied from spring-projects/spring-security#11275
Describe the bug
After upgrading spring-boot-starter-parent from version 2.6.4 to 2.7.0 and then making the suggested changes described in Spring Security without the WebSecurityConfigurerAdapter, specifically replacing the configuration class that extends WebSecurityConfigurerAdapter with a configuration class that declares a bean of type SecurityFilterChain, classes annotated with @WebMvcTest no longer auto-configure Spring Security.

As a temporary fix I've included @ImportAutoConfiguration(classes = SecurityConfig.class) pointing to the SecurityConfig.class that contains the SecurityFilterChain bean declaration

To Reproduce

  1. Configure Spring Security with a SecurityFilterChain bean using an oauth2 provider
  2. Configure the HttpSecurity to set the default AuthenticationEntryPoint to alway return a 401 (instead of the default redirect to the form login page) with .defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), AnyRequestMatcher.INSTANCE))
  3. Write a @WebMvcTest test class to test the error response of an unauthenticated request to a secured endpoint
  4. Observer that the response is a 302 and not the expected 401 (since the request was redirected to the form login page)
  5. Run the application and test the same endpoint - observe that the response is 401
  6. Further - placing a breakpoint at the beginning of the commence method in DelegatingAuthenticationEntryPoint when running the test can confirm that the HttpStatusEntryPoint that was configured is not included in the list of entryPoints

Expected behavior
@WebMvcTest or @AutoConfigureMockMvc should include beans of type SecurityFilterChain in auto-configuration

Sample
No sample

@wilkinsona
Copy link
Member

Thanks for the report. SecurityFilterChain beans are already including in a @WebMvcTest and we have tests that verify this is the case including this one.

Unfortunately, there are too many unknowns for us to be able to diagnose the problem from your description alone. If we pieced together a sample, there's no guarantee that it would reproduce the exact same problem that you are seeing.
If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that you know reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label May 25, 2022
@hpoettker
Copy link
Contributor

hpoettker commented May 25, 2022

I can confirm that this problems exists.

Implementations of WebSecurityConfigurerAdapter are usually annotated with @Component. They are picked by component scanning and included by WebMvcTypeExcludeFilter.

SecurityFilterChain beans are usually built in bean factory methods in classes annotated with @Configuration. The WebMvcTypeExcludeFilter does indeed include SecurityFilterChain but it won't see beans of that type because it filters away the configuration class. The configuration class currently needs to be added manually with @Import. Otherwise, the auto-configured SecurityFilterChain will kick in.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 25, 2022
@wilkinsona
Copy link
Member

Having looked at #31165 and the sample provided there, I believe I now understand what's happening here too.

specifically replacing the configuration class that extends WebSecurityConfigurerAdapter with a configuration class that declares a bean of type SecurityFilterChain, classes annotated with @WebMvcTest no longer auto-configure Spring Security

Unlike a class that extends WebSecurityConfigurerAdapter, a plain @Configuration class won't be included in a @WebMvcTest by default.

As a temporary fix I've included @ImportAutoConfiguration(classes = SecurityConfig.class) pointing to the SecurityConfig.class that contains the SecurityFilterChain bean declaration

Other than using @Import rather than @ImportAutoConfiguration, this is what you should be doing and what is already recommended in the reference documentation since these changes in 2.5.11 for #16088.

@wilkinsona wilkinsona added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels May 25, 2022
@hpoettker
Copy link
Contributor

I think this might be a noteworthy point for the release notes.

The migration experience can be quite puzzling when analyzing the failing @WebMvcTest tests that are missing the import. Especially in cases where the auto-configured SecurityFilterChain kicks in. Many people's first guess will be that they made a mistake in the migration until they realize what's actually happening.

@wilkinsona
Copy link
Member

I've added a section to the release notes.

@wilkinsona wilkinsona added type: wiki-documentation A documentation update required on the wiki and removed status: invalid An issue that we don't feel is valid labels May 25, 2022
@adase11
Copy link
Author

adase11 commented May 25, 2022

@wilkinsona thanks for looking into this and for the feedback. Would it be viable to include configuration classes annotated with @EnableWebSecurity in WebMvcTypeExcludeFilter? I would imagine that a large number of configuration classes that define SecurityFilterChain beans are annotated with @EnableWebSecurity instead of base @Configuration. This would eliminate the necessity to add import statements to all broken tests after a migration

@wilkinsona
Copy link
Member

Thanks for the suggestion. You don't have to use @EnableWebSecurity as Boot will auto-configure it for you so it may not help everyone. It may also hinder some people if they've used it on a configuration class that defines beans that they don't want to be included in a sliced test. We'll consider it though.

@wilkinsona wilkinsona added the for: team-meeting An issue we'd like to discuss as a team to make progress label May 25, 2022
@adase11
Copy link
Author

adase11 commented May 25, 2022

Makes sense, thanks

@wilkinsona
Copy link
Member

We've just discussed this and decided that the potential problems of including @EnableWebSecurity-annotated beans outweigh the benefits.

@wilkinsona wilkinsona removed the for: team-meeting An issue we'd like to discuss as a team to make progress label May 25, 2022
@adase11
Copy link
Author

adase11 commented May 25, 2022

Thanks for considering

@JogoShugh
Copy link

I am having this issue too, but no success solving yet. I have a test like this:

@SpringBootTest
@Import(TestConfiguration::class, WebSecurityConfig::class)
@ExtendWith(RestDocumentationExtension::class, SpringExtension::class)
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
class ScaApplicationTests(

Where WebSecurityConfig is my class like this:

@EnableWebSecurity
@Configuration
class WebSecurityConfig(private val scaTransactionSecurityFilter: ScaTransactionSecurityFilter) {
    @Bean
    fun filterChain(http: HttpSecurity) : SecurityFilterChain {
        val oauth2ResourceServerCustomizer: Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> =
            Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> {
                it.jwt()
            }

        return http
                .authorizeRequests().anyRequest().authenticated()
                .and()
                .oauth2ResourceServer(oauth2ResourceServerCustomizer)
                .addFilterAfter(scaTransactionSecurityFilter, BearerTokenAuthenticationFilter::class.java)
                .build()
    }

    @Bean
    fun jwtDecoder(): JwtDecoder? {
        class NonValidatingJWTProcessor : DefaultJWTProcessor<SecurityContext>() {
            override fun process(signedJWT: SignedJWT, context: SecurityContext?): JWTClaimsSet = signedJWT.jwtClaimsSet
        }
        return NimbusJwtDecoder(NonValidatingJWTProcessor())
    }

    fun getUser(token: Any): ConcurUser? {
        return if (token is Jwt) {
            ConcurUser.fromJwt(token)
        } else null
    }
}

This setup is working well with bootRun , but not under test.

Under test, the filterChain bean function does run, but by the time the mockmvc request is made, the code I just get a 401, and the custom filter I injected until the chain never runs.

@wilkinsona
Copy link
Member

@JogoShugh I can't seen the connection with this issue. It's specific to WebMvcTest which won't pick up a SecurityFilterChain defined as a @Bean. When using @SpringBootTest, every bean in your application should be used even without your import of WebSecurityConfig. If you'd like us to investigate further, please open a new issue with a minimal example written in Java that reproduces the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: wiki-documentation A documentation update required on the wiki
Projects
None yet
Development

No branches or pull requests

5 participants