spring-security: SwitchUserFilter not working in Spring Security 6

Describe the bug When using Spring Security 6 (via the Spring Boot 3 BOM) the SwitchUserFilter is not working anymore. The currently logged in user is redirected to the SwitchUserUrl (that is configured in the SwitchUserFilter), but the user is not switched.

The attached log file shows the following line: “Failed to find original user”

To Reproduce

  • Have a Spring Boot 3 project with Spring Security
  • Define a SwitchUserFilter bean in a configuration class:
	@Bean
	public SwitchUserFilter switchUserFilter() {
		SwitchUserFilter filter = new SwitchUserFilter();
		filter.setUserDetailsService(userDetailsService);
		filter.setUsernameParameter("username");
		filter.setSwitchUserUrl("/admin/switch_user");
		filter.setExitUserUrl("/admin/switch_user_exit");
		filter.setTargetUrl("/");
		return filter;
	}
  • Use this bean in a SecurityFilterChain:
.addFilterAfter(switchUserFilter(), AuthorizationFilter.class)
  • Login as an admin user and try to switch to a different user

Expected behavior The user performing the switch should be logged in as the selected user.

Sample While I don’t have a minimal example, I have an open source project that reproduces the issue. The relevant config is here: https://gitlab.com/skrupeltng/skrupel-tng/-/blob/issue-531_spring_boot_3/src/main/java/org/skrupeltng/config/SecurityConfig.java

The javadoc of the SwitchUserFilter still states: “Note that the filter must come after the FilterSecurityInteceptor in the chain” However, FilterSecurityIntercepter is deprecated. The deprecation text says one should use AuthorizationFilter, so I used this. Using the AuthorizationFilter was in fact working when using Spring Boot 2.7 and Spring Security 5.8. Maybe we have to put the SwitchUserFilter before/after a different Filter now?

switch_user.log

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 1
  • Comments: 16 (7 by maintainers)

Commits related to this issue

Most upvoted comments

I am not clear yet on whether or not this is intended behavior and using requireExplicitSave(false) is required now when using the SwitchUserFilter, or if this is considered a bug. In other words: should I close this issue now? 😃

I think it is a bug, I’ll just confirm with the team first and proceed with the fix.

Thank you for your response! I just tried this workaround and it works! I am now able to continue with the migration to Spring 3 and Spring Security 6 😃

I created a minimal test project demonstrating the issue: https://gitlab.com/robertbleyl/switch-user-test/-/tree/main The readme contains the full description of how to set it up. I hope this helps in either debugging or finding the issue in how I set it up 😃

After upgrading spring boot from 2.7 to 3.0.4 I’m experiencing the same issue. After switching the user doesn’t get changed.

My code is very similar to @RobertBleyl so not sharing it here. Can this issue be reopened?

Thanks.

I am still experiencing the issue with Spring Boot 3.0.3 (that comes with Spring Security 6.0.2 which appears to include the changes made for this issue).

Attached a log file with trace information: switch_user.log

The same debug output appears as before: Failed to find original user

The current version of my SecurityConfig can be found here.

I removed the previously added line .securityContext(c -> c.requireExplicitSave(false)) again because this caused a lot of other issues related to the login process. It produced a lot of output like this:

w.c.HttpSessionSecurityContextRepository : Failed to create a session, as response has been committed. Unable to store SecurityContext.

Is this “requireExplicitSave” necessary now or was this just for debugging purposes? Am I using the SwitchUserFilter wrong somehow or is there still a bug?

Hi @RobertBleyl, thanks for the report.

I think that the SwitchUserFilter is not explicitly saving the security context in the SecurityContextRepository.

Can you add the following configuration and see if it solves the problem? If so, that confirms my suspicion.

public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.securityContext((securityContext) -> securityContext
			.requireExplicitSave(false)
		);
	return http.build();
}