spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.5k stars 5.78k forks source link

Deprecate WebSecurityConfigurerAdapter #10822

Closed eleftherias closed 2 years ago

eleftherias commented 2 years ago

With the following issues closed we've added the ability to configure the security of an application without needing the WebSecurityConfigurerAdapter.

Related issues:


Background

Below is additional information on why WebSecurityConfigurerAdapter is being deprecated.

WebSecurityConfigurerAdapter and HttpSecurity DSL were created to work around https://github.com/spring-projects/spring-framework/issues/18353 Unfortunately, WebSecurityConfigurerAdapter design is flawed causing quite a few problems that cannot be properly fixed.

Does Not Expose Beans

The first is that rather than exposing Beans, it silently creates objects that cannot be used by the underlying application or by Spring Boot for auto configuration. This makes it difficult for Boot to know if an AuthenticationManager needs to be created or not. It also makes it difficult for users to leverage an AuthenticationManager in their own code as a Bean.

Instead, it is preferred that users expose an AuthenticationManager (or AuthenticationProvider or UserDetailsService) as a Bean so it can be used by the rest of the application.

Beans Cannot be Injected via Method Arguments

Another issue with WebSecurityConfigurerAdapter is that the static nature of the method signatures means that Security configuration does not follow best practices of passing in the Beans by method arguments. Instead, dependencies must be injected into member variables or resolved through method references.

While there is nothing inherently wrong with this, it doesn't follow best practices causing several limitations.

When using method arguments for Bean dependencies Spring knows exactly which Beans are dependencies before Object creation takes place. This means the minimal Object graph can be created. When using member variables, @Autowired methods on the configuration class, etc Spring needs to initialize the entire @Configuration to resolve the Bean. Similar problems happen when using method references to resolve Beans.

Not being able to determine the minimal dependencies of a Bean leads to BeanCurrentlyInCreationException (e.g. gh-4489 ) and AlreadyBuiltException being thrown (e.g. gh-3916). Again, this happens in large part due to not having as much information about the Object graph since parameters are not used to inject the Bean dependencies.

linghengqian commented 2 years ago

Thanks for your efforts, I noticed that https://docs.spring.io/spring-security/reference/5.7.0-M1/servlet/configuration/java.html documentation has not been updated, so I am a little confused, something like this How should the scene migrate WebSecurityConfigurerAdapter to SecurityFilterChain. The TokenAuthFilter here extends BasicAuthenticationFilter, so the construction requires an AuthenticationManager parameter.

  @Order(1)
  @Configuration
  public static class ApiWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
         @Override
         protected void configure(HttpSecurity http) throws Exception {
                 http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                         .addFilter(new TokenAuthFilter(super.authenticationManager()));
         }
}
eleftherias commented 2 years ago

Thanks for bringing this up @linghengqian.

The reference documentation was updated as part of gh-10003 and will be available in 5.7.0-M2.
At the moment it's available in the latest snapshot documentation https://docs.spring.io/spring-security/reference/5.7/servlet/configuration/java.html.

We will also share a blog post on how to migrate common use-cases when we release 5.7.0-M2.

The configuration you share above can be rewritten as follows:

@Configuration
public class SecurityConfiguration {

    @Bean
    @Order(1)
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // you probably want a request matcher since you are using @Order
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                .apply(customDsl());
        return http.build();
    }
}

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
        http.addFilter(new TokenAuthFilter(authenticationManager));
    }

    public static MyCustomDsl customDsl() {
        return new MyCustomDsl();
    }
}
eleftherias commented 2 years ago

Closed via e97c6438705172c64be8f10ddce9dd7b10357126

eleftherias commented 2 years ago

The related blog post containing common use-cases https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

NotFound403 commented 2 years ago

huge change

tha2015 commented 2 years ago

It is not a good practice to both modify the input parameter and return a value. I would not write some method like this if I have a choice.

linghengqian commented 2 years ago

It is not a good practice to both modify the input parameter and return a value. I would not write some method like this if I have a choice.

If you need to configure Multiple HttpSecurity, you'll find WebSecurityConfigurerAdapter not intuitive to the code and add a layer of burden. To be honest, WebFlux's thinking in this area is pretty good and worth changing.

tha2015 commented 2 years ago

configure() method returns void, so I understand that it will create side effects. The method securityFilterChain() returns a value, so it should not create side effects (changing input). Is there any chance to improve this method?

eleftherias commented 2 years ago

@tha2015 The HttpSecurity bean has the scope prototype which means a new bean instance is created every time a request for that specific bean is made. This is the same pattern we use in Reactive applications. What is the reason you think this pattern will create side effects?

tha2015 commented 2 years ago

When a method modifying its parameters, it is causing side effects.

I had a look to how webflux and security is configured. One example is

return http.authorizeExchange() .anyExchange().authenticated() .and().build();

which looks better because it is like a builder pattern and from this snippet, I don't see the parameter is modified (although it might actually do the same , i.e. modifying the parameter, as you said).

Your example clearly shows that input is modified and a value is returned. I am not saying it doesn't work. But this design could be improved by either returning a value without modifying input, or using parameter as in/out parameter and returning void.

tha2015 commented 2 years ago

Actually builder pattern does modify the builder objects, so my thought about reactive example is not totally correct.

Still I believe this design is confusing and we should try to improve it. From my perspective, a method should either:

I would not write a method which both modifying parameters and returning results.

arlyon commented 2 years ago

Hi,

I have a function to configure the AuthenticationManager like so:

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
  }

  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

What is the recommended approach now? As I understand it I need to just have an AuthenticationManager @Bean but I don't see an equivalent in the above post. I would use AuthenticationManagerBuilder::performBuild() but that is protected. Do I have to write my own in this case? Thanks

Edit:

Didn't notice the .and().build() api. New config is as follows:

  @Bean
  AuthenticationManager authenticationManager(
      AuthenticationManagerBuilder builder, PasswordEncoder encoder) throws Exception {
    return builder.userDetailsService(userDetailsService).passwordEncoder(encoder).and().build();
  }

  @Bean
  public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
  }
eleftherias commented 2 years ago

@arlyon There is no need to explicitly set the userDetailsService and passwordEncoder on the AuthenticationManager. You can register them as beans and the default AuthenticationManager created by Spring Security will pick them up:

@Bean
public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

@Bean
public InMemoryUserDetailsManager userDetailsService() {
      UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
      return new InMemoryUserDetailsManager(user);
}

No need to configure anything on the AuthenticationManager.

Let us know if you run into issues with the above configuration.

linghengqian commented 2 years ago

According to https://docs.spring.io/spring-security/reference/5.7.0-M3/servlet/appendix/namespace/authentication-manager.html, I don't see much response to AuthenticationManagerBuilder.eraseCredentials(false), is this intentionally ignored? The following configuration exists in Spring Security 5.6.2, but I'm having a hard time thinking how to configure it in Spring Security 5.7.0-M3.

@Order(1)
@Configuration
public static class ApiWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
           @Override
           protected void configure(HttpSecurity http) throws Exception {
                http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
                      .httpBasic(withDefaults());
            }

          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.eraseCredentials(false);
          }
}
arlyon commented 2 years ago

@eleftherias thanks for the quick reply. I am using an AbstractHttpConfigurer and everything seems to be alright, the only problem is the ProviderManager that gets injected from http.getSharedObject(AuthenticationManager.class) has a single provider attached, namely the AnonymousAuthenticationProvider. Registering beans (in any combination I could think of so far) does not change this. Is there anything obvious I may have missed?

Could you point me to somewhere in the codebase where this is supposed to happen so I may debug?

elfwine commented 2 years ago

@arlyon There is no need to explicitly set the userDetailsService and passwordEncoder on the AuthenticationManager. You can register them as beans and the default AuthenticationManager created by Spring Security will pick them up:

@Bean
public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

@Bean
public InMemoryUserDetailsManager userDetailsService() {
      UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
      return new InMemoryUserDetailsManager(user);
}

No need to configure anything on the AuthenticationManager.

Let us know if you run into issues with the above configuration.

I have the same problem with @arlyon and I've been seeing the above @Bean UserDetailsService, but I got an error "No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken" whenever I tried to submit the login page. Instead of InMemoryUserDetailsManager, I replaced it with my custom service.

@Bean
public CustomUserDetailsService userDetailsService() {
    return new CustomUserDetailsService();
}

The new configuration is really quite confusing. There's no real transition from using WebSecurityConfigurerAdapter. I'm sure I'm not the only one who is scratching head for this changes.

eleftherias commented 2 years ago

Thanks everyone for trying out the new configuration style! The conversation on this issue has gotten quite long and disorganized so I ask everyone that has a question or needs help with their configuration to ask on Stack Overflow.

We have a team member monitoring the spring-security tag on Stack Overflow and you will get an answer faster. Having the questions on Stack Overflow also makes them more organized and searchable for anyone else that has the same issue.

@arlyon and @elfwine if you're still having trouble feel free to post the quest on Stack Overflow and I, or someone else for the team, will answer them there.

MinchukSergei commented 2 years ago

@eleftherias, hi. How can I config multiple instances of SecurityFilterChain? I have 2 places in project where I have HttpSecurity configuration. And I want to use the same HttpSecurity to extend my configuration but not override it.

parthbhagat commented 2 years ago

Have followed document from here to replace WebSecurityConfigurerAdapter with SecurityFilterChain. But it does not seems to be working. I've posted question on StackOveflow. @eleftherias Can you please identify where it is wrong?

Sparklll commented 2 years ago

If anybody else have problems with AuthenticationManager bean and have issue like a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.

try to use

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
netssfy commented 2 years ago

If anybody else have problems with AuthenticationManager bean and have issue like a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.

try to use

   @Bean
   public AuthenticationManager authenticationManager(
           AuthenticationConfiguration authenticationConfiguration) throws Exception {
       return authenticationConfiguration.getAuthenticationManager();
   }

This is the right answer and replacement of Depracate WebSecurityConfigurerAdapter

manofthepeace commented 2 years ago

@Sparklll @netssfy May I know how you guys re using that? I am trying to migrate to this new way and using

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new AuthService();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
            throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

Ends up in a stackOverflowException

java.lang.StackOverflowError: null
    at java.base/java.lang.String.equals(String.java:1830)
    at org.springframework.util.ReflectionUtils.isEqualsMethod(ReflectionUtils.java:518)
    at org.springframework.aop.support.AopUtils.isEqualsMethod(AopUtils.java:151)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:167)
    at jdk.proxy2/jdk.proxy2.$Proxy260.authenticate(Unknown Source)
    at jdk.internal.reflect.GeneratedMethodAccessor286.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)

in the getAuthenticationManagerBean() call within the AuthenticationConfiguration I see to get a proxy error. Anything else would be missing? Thanks

Sparklll commented 2 years ago

@manofthepeace I did it exactly the same way. I'm not sure if it would help but check if you have @EnableWebSecurity on your config class.

manofthepeace commented 2 years ago

@Sparklll thanks for your reply. I do have it, as it worked also before with the WebSecurityConfigurerAdapter

this is what I have;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class HttpSecurityConfig {
Sparklll commented 2 years ago

@manofthepeace Hm... I have exactly the same configuration and everything works. The only difference is that UserDetailsService is used not as a @Bean in config class, but as a @Component annotation on concrete class. But I don't think there's much difference here. And I have no errors with this kind configuration. Probably there is another problem somewhere

jackgon7 commented 2 years ago

@Sparklll @netssfy May I know how you guys re using that? I am trying to migrate to this new way and using

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new AuthService();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
            throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

Ends up in a stackOverflowException

java.lang.StackOverflowError: null
  at java.base/java.lang.String.equals(String.java:1830)
  at org.springframework.util.ReflectionUtils.isEqualsMethod(ReflectionUtils.java:518)
  at org.springframework.aop.support.AopUtils.isEqualsMethod(AopUtils.java:151)
  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:167)
  at jdk.proxy2/jdk.proxy2.$Proxy260.authenticate(Unknown Source)
  at jdk.internal.reflect.GeneratedMethodAccessor286.invoke(Unknown Source)
  at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.base/java.lang.reflect.Method.invoke(Method.java:568)
  at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)

in the getAuthenticationManagerBean() call within the AuthenticationConfiguration I see to get a proxy error. Anything else would be missing? Thanks

Have you managed to solve the error? Facing the same problem now.

manofthepeace commented 2 years ago

@jackgon7 Yes I was able to solve it thanks to @Sparklll which made me double check the code with his last comment.

My custom UserDetailsService was already annotated with @Service. Defining the bean in the @Configuration was not ok/redundant. I have no idea why/how it could work before but anyway. I have removed that bean creation from my security configuration then it worked.

Please also be cautious where you autowire your AuthenticationManager. Due do somewhat bad design I had to annotate it with @Lazy for things to work. It was a smelly design which I have adapted to not have to have it lazy now, but you may have to double check that as well.

jackgon7 commented 2 years ago

@jackgon7 Yes I was able to solve it thanks to @Sparklll which made me double check the code with his last comment.

My custom UserDetailsService was already annotated with @Service. Defining the bean in the @Configuration was not ok/redundant. I have no idea why/how it could work before but anyway. I have removed that bean creation from my security configuration then it worked.

Please also be cautious where you autowire your AuthenticationManager. Due do somewhat bad design I had to annotate it with @Lazy for things to work. It was a smelly design which I have adapted to not have to have it lazy now, but you may have to double check that as well.

Thanks for the reply. I found out the problem was due to I'm using AuthenticationManager in my @RestController as you mentioned. I managed to solve it by using returning a ProviderManager in the AuthenticationManager @Bean in my @Configuration as follow:

@Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
        authenticationProviders.add(customerAuthProvider);
        authenticationProviders.add(userAuthProvider);
        return new ProviderManager(authenticationProviders);
    }

But I'm not quite sure if it is best practice though. I'm still quite new in the Spring environment.

linghengqian commented 2 years ago

@jackgon7 Your configuration confuses me a bit, and while I'd suggest hanging up to Stackoverflow to continue the discussion, there's actually no need to bother if you just need to instantiate an AuthenticationManager bean for SecurityFilterChain to use, because AuthenticationManagerBuilder has always been in the context of Spring Security. Let an example of a simple modification.

@Bean 
 public SecurityFilterChain otherSecurityFilterChain(HttpSecurity http) throws Exception { 
         http.authorizeRequests((requests) -> requests.anyRequest().hasAnyRole("CUSTOM")); 
         AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); 
         http.authenticationManager(authenticationManagerBuilder.eraseCredentials(true).build());
         return http.build(); 
}
mohantangirala commented 2 years ago

Hello, Do you have any documentation for SAML2 config?

hannah23280 commented 1 year ago

Hi everyone, Any documents that show sample codes of how to add our own custom AuthenticationProviders given that the WebSecurityConfigurerAdapter is now deprecated

Rutito2010 commented 1 year ago

@jackgon7 Yes I was able to solve it thanks to @Sparklll which made me double check the code with his last comment. My custom UserDetailsService was already annotated with @Service. Defining the bean in the @Configuration was not ok/redundant. I have no idea why/how it could work before but anyway. I have removed that bean creation from my security configuration then it worked. Please also be cautious where you autowire your AuthenticationManager. Due do somewhat bad design I had to annotate it with @Lazy for things to work. It was a smelly design which I have adapted to not have to have it lazy now, but you may have to double check that as well.

Thanks for the reply. I found out the problem was due to I'm using AuthenticationManager in my @RestController as you mentioned. I managed to solve it by using returning a ProviderManager in the AuthenticationManager @bean in my @configuration as follow:

@Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
        authenticationProviders.add(customerAuthProvider);
        authenticationProviders.add(userAuthProvider);
        return new ProviderManager(authenticationProviders);
    }

But I'm not quite sure if it is best practice though. I'm still quite new in the Spring environment.

@jackgon7 Yes I was able to solve it thanks to @Sparklll which made me double check the code with his last comment.

My custom UserDetailsService was already annotated with @Service. Defining the bean in the @Configuration was not ok/redundant. I have no idea why/how it could work before but anyway. I have removed that bean creation from my security configuration then it worked.

Please also be cautious where you autowire your AuthenticationManager. Due do somewhat bad design I had to annotate it with @Lazy for things to work. It was a smelly design which I have adapted to not have to have it lazy now, but you may have to double check that as well.

Thanks for this explanation, i been with this problem for 3 days and all i got to do was remove the @Bean anotation from my inmemoryuserdetails i have in the websecurityconfig

hnpanther commented 1 year ago

I'm using AuthenticationManager in RestController and use this code for expose AuthenticationManager

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .cors()
                .and().csrf().disable()
                .headers().frameOptions().disable()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeRequests().antMatchers("/api/test/**", "/auth/**", "/h2-console/**").permitAll()
                .and().authorizeRequests().anyRequest().authenticated()
                .and().addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
        http.authenticationProvider(customAuthProvider);
        http.authenticationProvider(ldapCustomProvider);
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

but this AuthenticationManager dosn't contain my custom provider. what's problem? and what's best way to expose AuthenticationManager ?

dmak commented 1 year ago

Dear all, I am facing the issue which is not obvious to resolve just by reading the documentation. I have defined the custom configuration in my Spring Boot application like below:

@Configuration
@EnableWebSecurity
public class CustomSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.
            csrf().disable().
            logout().disable().
            authorizeRequests().anyRequest().permitAll();

        return http.build();
    }
}

The above method is called, however has no effect as SecurityFilterChain instance created by OAuth2SecurityFilterChainConfiguration is used instead. Debug log:

2022-10-20 15:49:48.790 [main] o.s.b.a.s.DefaultWebSecurityCondition    : Condition DefaultWebSecurityCondition on org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration matched due to AllNestedConditions 2 matched 0 did not; NestedCondition on DefaultWebSecurityCondition.Beans @ConditionalOnMissingBean (types: org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter,org.springframework.security.web.SecurityFilterChain; SearchStrategy: all) did not find any beans; NestedCondition on DefaultWebSecurityCondition.Classes @ConditionalOnClass found required classes 'org.springframework.security.web.SecurityFilterChain', 'org.springframework.security.config.annotation.web.builders.HttpSecurity'
2022-10-20 15:49:48.791 [main] a.ConfigurationClassBeanDefinitionReader : Registered bean definition for imported class 'org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration'
2022-10-20 15:49:48.792 [main] o.s.b.a.condition.OnBeanCondition        : Condition OnBeanCondition on org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration#jwtSecurityFilterChain matched due to @ConditionalOnBean (types: org.springframework.security.oauth2.jwt.JwtDecoder; SearchStrategy: all) found bean 'jwtDecoderByJwkKeySetUri'
...
2022-10-20 15:49:49.082 [main] a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method com.mycompany.CustomSecurityConfig.filterChain()
...
2022-10-20 15:49:52.276 [main] edFilterInvocationSecurityMetadataSource : Adding web access control expression [authenticated] for any request
2022-10-20 15:50:13.348 [main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@33502cfe, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@729d1428, org.springframework.security.web.context.SecurityContextPersistenceFilter@7d0312a, org.springframework.security.web.header.HeaderWriterFilter@6ca97ddf, org.springframework.security.web.csrf.CsrfFilter@38f569d, org.springframework.security.web.authentication.logout.LogoutFilter@1104ad6a, org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter@74ab8610, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7833407, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@66acaa54, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@115924ba, org.springframework.security.web.session.SessionManagementFilter@6a905513, org.springframework.security.web.access.ExceptionTranslationFilter@5749e633, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@49741e80]
...
2022-10-20 15:50:13.384 [main] edFilterInvocationSecurityMetadataSource : Adding web access control expression [permitAll] for any request
2022-10-20 15:50:17.641 [main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@4a0f4282, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@19d3f4fb, org.springframework.security.web.context.SecurityContextPersistenceFilter@99f75e4, org.springframework.security.web.header.HeaderWriterFilter@118c1faa, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2b6ff016, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5aefdb9e, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@43cf97a8, org.springframework.security.web.session.SessionManagementFilter@da5b46f, org.springframework.security.web.access.ExceptionTranslationFilter@11267e87, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@7827cdfc]

Is it expected that the bean CustomSecurityConfig.filterChain does not participate in DefaultWebSecurityCondition evaluation? Or the issue with DefaultWebSecurityCondition is that the instance of WebSecurityConfigurerAdapter is not in the context anymore (as to this issue it is deprecated)?

The solution was to add Order(1):

@Configuration
@EnableWebSecurity
public class CustomSecurityConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

and the application started to work as expected.

The documentation I read before posting:

UPD: Posted to stackoverflow.

ottlinger commented 1 year ago

I'm running into issues as I have a locally defined AuthenticationFilter implementation.

The now deprecated/old way was:

    public LoginTenantAuthenticationFilter authenticationFilter() throws Exception {
        LoginTenantAuthenticationFilter filter = new LoginTenantAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManager());
        filter.setAuthenticationFailureHandler(failureHandler());
        return filter;
    }

But I'm unable to wire in my AuthenticationManager instance as it is null.

The definition of the filter happens in my

@Configuration
@EnableMethodSecurity(prePostEnabled = true) // to make PreAuthorize work
@EnableWebSecurity
public class AuthenticationConfiguration {

which is why I'm unable to wire in the authentication manager as suggested above.

@eleftherias unfortunately I am unable to find a comparable issue on StackOverflow. Hoping you can help somehow or point me to an example that works with SpringBoot3 and Spring6. Thanks.

kyle-copeland commented 1 year ago
@Configuration
public class SecurityConfiguration {

    @Bean
    @Order(1)
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // you probably want a request matcher since you are using @Order
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                .apply(customDsl());
        return http.build();
    }
}

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
  @Override
  public void configure(HttpSecurity http) throws Exception {
      AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
      http.addFilter(new TokenAuthFilter(authenticationManager));
  }

  public static MyCustomDsl customDsl() {
      return new MyCustomDsl();
  }
}

Thank you so much for posting this! It saved me big time.

halilkrkn commented 1 year ago

Hi, If you are living to problem about using AuthenticationManagerBuilder without extends WebSecurityConfigurerAdapter and taken a problem like Error creating bean with name 'webSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?

You can use add the spring.main.allow-circular-references=true into application.properties.xml.

@Configuration
public class SecurityConfiguration {

    @Bean
    @Order(1)
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // you probably want a request matcher since you are using @Order
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                .apply(customDsl());
        return http.build();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
    }
}

OR, you can use this method

@Configuration
public class SecurityConfiguration extends GlobalAuthenticationConfigurerAdapter{

    @Bean
    @Order(1)
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // you probably want a request matcher since you are using @Order
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                .apply(customDsl());
        return http.build();
    }

  @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }
}
maksymgendin commented 1 year ago

What about default configuration of HttpSecurity? Currently the WebSecurityConfigurerAdapter sets it:

image

How can I disable these defaults without extending WebSecurityConfigurerAdapter and using it's parameterized constructor?

image

DenkovychOleh commented 10 months ago

@arlyon There is no need to explicitly set the userDetailsService and passwordEncoder on the AuthenticationManager. You can register them as beans and the default AuthenticationManager created by Spring Security will pick them up:

@Bean
public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

@Bean
public InMemoryUserDetailsManager userDetailsService() {
      UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
      return new InMemoryUserDetailsManager(user);
}

No need to configure anything on the AuthenticationManager.

Let us know if you run into issues with the above configuration.

Hello, I have problem My code: ` import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain;

@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public PasswordEncoder encoder() { return NoOpPasswordEncoder.getInstance(); } @Bean public InMemoryUserDetailsManager userDetailsService() { UserDetails user = User.builder() .username("user1") .password("user1") .roles("ADMIN") .build(); return new InMemoryUserDetailsManager(user); }

@Bean
protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .csrf((csrf) -> csrf.disable())
            .cors((cors) -> cors.disable())
            .authorizeHttpRequests(
                    (authorizationManagerRequestMatchers -> authorizationManagerRequestMatchers
                            .requestMatchers(HttpMethod.GET, "/", "/open").permitAll()
                            .requestMatchers(HttpMethod.GET, "/secure").hasAnyRole("ADMIN", "USER"))
            ).sessionManagement((sessionManagement) -> sessionManagement
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .build();
}

}

` When I send request in Postman I got 403 Error

ottlinger commented 10 months ago

When I followed the workarounds proposed in this issue I ran into https://github.com/spring-projects/spring-security/issues/13620 - does anyone else have similar issues? SpringBoot2 worked fine but SpringBoot3 does not work (the issue links to a reproducer app on Github / sb3-filter-problem) with a combination of a 3-part-login and SB3.