We should adapt the recommendations and examples in the blog article Spring Security without the WebSecurityConfigurerAdapter into the reference documentation.

For example, we can configure an AuthenticationManager for use by the application that can perform user authentication (similar to formLogin()) like so:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authorize) -> authorize
                .anyRequest().authenticated()
            );
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);

        return new ProviderManager(authenticationProvider);
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}

Context:

Many applications require the use of an AuthenticationManager outside the Spring Security filter chain (e.g. in a @RestController). The LDAP Authentication example recommends publishing an AuthenticationManager @Bean, and this example can be generalized and numerous examples given for various scenarios.

Comment From: sjohnr

Note: We should also enhance the deprecation notice in 5.7/5.8 to include a hint of where to get the same information. Perhaps a link to this reference documentation, or a simplified example method signature for a SecurityFilterChain bean.

Comment From: marcusdacoregio

When this is completed we should update the link to the blog post in WebSecurityConfigurerAdapter to a link to the reference documentation

Comment From: marcusdacoregio

https://github.com/spring-projects/spring-security/issues/12343 can provide more use cases to the documentation

Comment From: michael-simons

Additional scenarios:

  • Describe how to configure the global authentication manager (the parent of all others), i.e. how to disable credential erasure
  • Make the DSL easier to use (a DSL should guide the user and prevent double / tripple usage and weird things like in this screenshot which is me trying to figure out one of the many places one can either access the shared object or appereantly set the one to use… FlYIkcgXgAAJzjF )

Comment From: uniquejava

Here is my journey.

I want to use authenticationManager in my custom LoginController (for some simple JWT login),

Here is my user.

spring.security.user.name=user
spring.security.user.password={noop}password

Here is my curl test.

curl  -d "username=user&password=password" -vvv http://localhost:8080/api/v1/login
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /api/v1/login HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.87.0
> Accept: */*
> Content-Length: 31
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Fri, 07 Apr 2023 03:41:09 GMT
<
* Connection #0 to host localhost left intact

As you can see, it always gives me 403 forbidden error, but I already configured /login permitAll().

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.authorizeHttpRequests(auth -> auth
                .requestMatchers("/login").permitAll()
                .anyRequest().authenticated()
        );

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(new JwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

Why? I found a solution here: https://stackoverflow.com/questions/75768437/requestmatchers-permitall-does-not-work It suggests not use spring boot 3, but spring boot 2 🤣

After a lot of try and fail, I found that the following simplified version also works!

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

   @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

But once I remove @Bean public UserDetailsService userDetailsService() , it will throw jakarta.servlet.ServletException: Handler dispatch failed: java.lang.StackOverflowError

and forward the request to some /errorurl but /error by default is not in the permitAll(..) path, so a 403 forbidden is given to my curl

Looks like spring security uses the default DaoAuthenticationProvider in this case but not the default InMemoryUserDetailsService.

I think put the /error path to permitAll by default will make spring security experience much much better, because then the curl will clearly tell me it's 500 internal server error, and then I will not be using this keyword to search requestMatchers permitAll not work 😄

Comment From: uniquejava

The reason I didn't configure UserDetailsService at first because spring security gives me this impression: it always has something by default.

AuthenticationFilter -> UsernamePasswordAuthenticationFilter by default AutenticationManager -> ProviderManager by default AuthenticationProvider -> DaoAuthenticationProvider by default UserDetailsService -> InMemoryUserDetailsService by default

In my previous journey, once we exposed return config.getAuthenticationManager(); like so, then InMemoryUserDetailsService is not the default UserDetailsService.

Comment From: sjohnr

Sorry you had some trouble, @uniquejava.

Please see my initial comment on this issue for how I recommend publishing an AuthenticationManager @Bean. Additional ways to publish one are documented on the 5.8 migration guide under Publish an AuthenticationManager Bean.

Regarding the StackOverflowError, see this comment.

I think put the /error path to permitAll by default will make spring security experience much much better, because then the curl will clearly tell me it's 500 internal server error, and then I will not be using this keyword to search requestMatchers permitAll not work 😄

Thanks for the suggestion! However, we prefer to use a more secure posture by default. In Spring Security 6, for example, requests that are missing an authorization rule are actually denied by default. I would argue that it's better for you to learn about this than it would be to have a weaker security posture by default. Hopefully, you can see the point here. :wink:

If you have any further questions, please use Stack Overflow and feel free to share a link to the posted question so others can find it.

Comment From: nandorholozsnyak

Sorry you had some trouble, @uniquejava.

Please see my initial comment on this issue for how I recommend publishing an AuthenticationManager @Bean. Additional ways to publish one are documented on the 5.8 migration guide under Publish an AuthenticationManager Bean.

Regarding the StackOverflowError, see this comment.

I think put the /error path to permitAll by default will make spring security experience much much better, because then the curl will clearly tell me it's 500 internal server error, and then I will not be using this keyword to search requestMatchers permitAll not work smile

Thanks for the suggestion! However, we prefer to use a more secure posture by default. In Spring Security 6, for example, requests that are missing an authorization rule are actually denied by default. I would argue that it's better for you to learn about this than it would be to have a weaker security posture by default. Hopefully, you can see the point here. wink

If you have any further questions, please use Stack Overflow and feel free to share a link to the posted question so others can find it.

Hello there,

Just bumped into this "problem" after migrating from Spring Security 5.7.6 to Spring Security 6. The docs at first glance looked fine, but this stupid /error problem drove me crazy.

It started with a non-existing favicon.ico and ended with a double login in the background that set a lot of things back and fort for my users, but guess what, on Firefox it did not cause problems, on Chromium it DID!

I know the docs are clear but maybe for the error page there could be a simple example or an admonition that if you forgot to configure the paths properly, you can end up having HTTP 403 for or redirects (it depends on your config) for example for non existing static resources. I understand the logic behind the enforcement, but maybe a lot of developer will face it, and they might not be able to understand the reason behind that.

Comment From: sjohnr

Thanks for your input on this everyone, and sorry for the delay in getting this task done. I've updated the Username/Password Authentication page. It now includes a few full examples (those discussed on this issue), and also links to other pages organized by use case similar to other recent updates to the docs.

You can preview the 5.8 version here (also forward ported up through 6.2/main).

Feedback welcome! If you do see additional items to add, please feel free to open a new issue.