Describe the bug When trying to log in, if the log in successful, then it will redirect to the wrong URL: http:localhost:9090/error?continue here it must redirect to / URL, to the URL which it comes from, but it goes to /error?continue

To Reproduce The below code processes the wrong URL

    /**
     * Indicates the URL that the user agent used for this request.
     * @return the full URL of this request
     */
    @Override
    public String getRedirectUrl() {
        String queryString = createQueryString(this.queryString, this.matchingRequestParameterName);
        return UrlUtils.buildFullRequestUrl(this.scheme, this.serverName, this.serverPort, this.requestURI,
                queryString);
    }

matchingRequestParameterName = continue requestURI = error so it will become /error?continue

Expected behavior It can redirect to the URL which it comes from or redirect to root path /

Note: the previous version (5.3.4) works well

Comment From: marcusdacoregio

Hi @Bahramudin, thanks for the report.

Can you provide a minimal, reproducible sample so we can have a better view of what is happening?

Comment From: Bahramudin

@marcusdacoregio The steps are as below: Let's pretend, I am accessing page /user/list, but this page needs authentication to be logged in, so it will redirect me to the login page, then after a successful login, spring security will redirect me to the page from where I was requested (/user/list), but now it redirects me this URL: http://localhost:9999/error?continue. If it was logged in success or not, it will all redirect to this URL, which is wrong and the Springboot throws 404 for this URL. But in the previous version, it works well. I hope you understand the explanation.

Comment From: marcusdacoregio

I setup a basic application, with the following configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults())
            .formLogin(Customizer.withDefaults());
        return http.build();
    }

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

}

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

}

Then I performed a GET localhost:8080/hello, got redirected to localhost:8080/login, provided the credentials, and was redirected to localhost:8080/hello?continue on successful login. This seems to work perfectly fine for me. I think there is a piece in the puzzle that you are missing, which is why I asked for a reproducible sample.

Comment From: Bahramudin

@marcusdacoregio Thanks for the reply! Here I have tried a lot to figure out the problem, but I did not get any solution. So here I have no way, but only to send you some screen shots, which let you know the configuration I did, and the DEBUG information. The below screenshot is my configuration: Snipaste_2023-02-11_00-06-35

And this screenshot show the request URL already set /error, I have don nothing just started the application and sent a GET request by browser as http://localhost:9999/user/list OR http://localhost:9999 which these URLs both needs to be authenticated, but I get the below result, which is very a strange error, that when and where the error has been set the request URL?!

Snipaste_2023-02-10_23-51-07

So you can check these screenshots if figured out the problem. Thanks in advance!

Comment From: ujkim

I'm experiencing exactly the same issue. The DefaultSavedRequest object is already set to error?continue api path. I couldn't find where the assignment was.

Spring Boot 3.x and Spring Security 6.0.1 have many challenages.. Can I get a hint about the solution?

Comment From: marcusdacoregio

Is the request sent via a browser or another tool like Postman? What are the dependencies?

@ujkim are you able to provide a minimal, reproducible sample?

Comment From: ujkim

@marcusdacoregio Thank you for your quick response!

First, the controller looks like this:

@Controller
public class MainController {

    @GetMapping("/")
    public String home(@CurrentUser Account account, Model model) {
        if (account != null) {
            model.addAttribute(account);
        }
        return "index";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

CurrentUser Annotation is processed with the Annotation below

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")

The Spring Security Config class looks like this:

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authorize) -> authorize
                .requestMatchers("/", "/sign-up", "/login").permitAll()
                .requestMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated())
            .securityContext((securityContext) -> securityContext
                .requireExplicitSave(false));

        http.formLogin()
            .loginPage("/login").permitAll();

        http.logout()
            .logoutSuccessUrl("/");
        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web -> web.ignoring()
                            .requestMatchers("/node_modules/**")
                            .requestMatchers(PathRequest.toStaticResources().atCommonLocations()));
    }
}

The login function in AccountService performs authentication by generating a token value.

public void login(Account account) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
            new UserAccount(account),
            account.getPassword(),
            List.of(new SimpleGrantedAuthority("ROLE_USER")));
        SecurityContextHolder.getContext().setAuthentication(token);
    }

AccountService implements UserDetailsService (org.springframework.security.core.userdetails.UserDetailsService) I implemented the loadUserByUsername function to return a UserAccount object (UserDetails).

As you can see, I delegated the login authentication process to Spring Security in the account processing logic. Can you guess the root of the problem?

Comment From: marcusdacoregio

@ujkim it's hard to guess what is happening since there is a lot going on. Did you try adding logging.level.org.springframework.security=TRACE to your application.properties and check the console log for a clue on what is happening?

Comment From: ujkim

Thanks for the debugging suggestions, Thanks to you, I found a clue! `2023-02-14T12:05:06.776+09:00 TRACE 28068 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter : Sending AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=0748489BB9BABE02D3F40B08156C4525], Granted Authorities=[ROLE_ANONYMOUS]] to authentication entry point since access is denied

org.springframework.security.access.AccessDeniedException: Access Denied`

I found your post on Gitter and solved it by granting permissions to /error. https://app.gitter.im/#/room/#spring-security:matrix.org

Thank you so much for your help!

Do you have any idea why this problem only happened in Spring Security 6?

Comment From: marcusdacoregio

Yes, Spring Security 6 applies authorization to all dispatcher types, see https://docs.spring.io/spring-security/reference/5.8/migration/servlet/authorization.html#switch-filter-all-dispatcher-types.

I'll close this as solved. Thanks.

Comment From: Harrycheng1992

Thanks for the debugging suggestions, Thanks to you, I found a clue! `2023-02-14T12:05:06.776+09:00 TRACE 28068 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter : Sending AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=0748489BB9BABE02D3F40B08156C4525], Granted Authorities=[ROLE_ANONYMOUS]] to authentication entry point since access is denied

org.springframework.security.access.AccessDeniedException: Access Denied`

I found your post on Gitter and solved it by granting permissions to /error. https://app.gitter.im/#/room/#spring-security:matrix.org

Thank you so much for your help!

Do you have any idea why this problem only happened in Spring Security 6?

HI @ujkim , You mention you solve this issue, can you provide more detail about the code you solved?

Comment From: LargePixels

I struggled with this issue but managed to get it working. I'm using Spring Security with Facebook Oauth2. I configured a self signed local cert so I could visit dev.localhost.com directly. Close browser and I visit my root "/" which requires authentication. Issue only appeared when logged out and with a fresh instance of the browser.. Enabling TRACE logging provided no useful debugging information that I could tell.

My configuration originally looked like this.

@Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) //make development less painful.. .authorizeHttpRequests(auth -> { auth.requestMatchers("/").permitAll(); auth.requestMatchers("/favicon.ico").permitAll(); auth.anyRequest().authenticated() .shouldFilterAllDispatcherTypes(true); })

I went to the 6.2 documentation and pulled this example.

@Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) //make development less painfull.. .authorizeHttpRequests(authorize -> authorize .requestMatchers("/","/static/**", "/signup", "/about").permitAll() .anyRequest().authenticated() )

Perhaps there an issue with method chaining vs configuring via a lambda? I don't even recall where I got the original code from.

My pom.xml is using spring-boot-starter-parent version 3.1.3

Comment From: CJMobileApps

This post helped me solve my problem upgrading from Springboot 2.7 to 3 using Spring Security 6. Everything in Spring Security 6 is locked down by default. Set .shouldFilterAllDispatcherTypes(false) to false instead of true.

Read through the documentation given earlier on dispatchers https://docs.spring.io/spring-security/reference/5.8/migration/servlet/authorization.html#switch-filter-all-dispatcher-types .

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authorize) -> authorize
                .shouldFilterAllDispatcherTypes(false)
                .requestMatchers("/", "/sign-up", "/login").permitAll()
                .requestMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated())
            .securityContext((securityContext) -> securityContext
                .requireExplicitSave(false));

        http.formLogin()
            .loginPage("/login").permitAll();

        http.logout()
            .logoutSuccessUrl("/");
        return http.build();
    }

Comment From: dayannaclaudino

If anyone has this problem, I managed to solve it this way.

@GetMapping("/home") public ModelAndView home() { ModelAndView mv = new ModelAndView("home"); return mv; }

Comment From: wimdeblauwe

I had the same issue where my application would redirect to /error after login. I solved it by adding this line:

.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()

In context:

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authz -> authz
                        .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                        .requestMatchers("/svg/*").permitAll()
                        .requestMatchers("/img/*").permitAll()
                        .anyRequest().authenticated())
                .formLogin(login -> login.loginPage("/login").permitAll())
                .logout(LogoutConfigurer::permitAll);

        return http.build();
    }

There was no need to set the shouldFilterAllDispatcherTypes to false.

Comment From: kumarimanjari

I have problem in this area. I have below scenario - 1. Access the home page, login page will come, user will be redirected to home page back once successful authentication. 2. I have a Vue 3 application having routes for example - /someOperation?queryParam1=value1&queryParam2=value2.. and so on. When I access this URL - it internally calls a Spring Boot API to perform an operation and then once this is successful it actually opens up the the component in FE. Here login part is working but I am not able to load the component of Vue, it ends with 404 -- I tried using SavedRequest which holds the Saved Request and accordingly redirect it using custom success handler How to load the VUE JS routes after successful authentication process, is there any way to forward the request and load the component.

My Spring Security contains minimal set up using security matchers for /** and anyRequest authenticated with ouath2 success handler.

Spring Security version - 6.2.1
Spring Boot - 3.2.1
Vue JS - 3
Vue JS router - 4

Can someone suggest something here.

Comment From: wimdeblauwe

@kumarimanjari I don't think your issue has anything to do with this issue. This issue is about Spring Security in the context of a Spring MVC application, so without a separate frontend. Maybe ask your question on stackoverflow?

Comment From: kumarimanjari

@wimdeblauwe Thank you for the response.

I do have a Spring MVC in my project only thing is that I don't have any controller which handles http://localhost:8080<base_path>/someOperation?queryParam1=value1&queryParam2=value2.. If I don't include Spring Security then I am able to open the required component with all the APIs calls. Why when I try to redirect using SavedRequest.redirectURL to FE, it gives me 404 saying no static resources found?

I you have any idea on this it would be really helpful.