Browse Source

Add RequestMatcher MigrationPath for SwitchUserFilter

To simplify migration, the filter's setter methods still use AntPathRequestMatcher.
Users can call the equivalent RequestMatcher setter methods to opt-in to the change early.

Issue gh-16417
Josh Cummings 4 months ago
parent
commit
15d9c13984

+ 43 - 7
docs/modules/ROOT/pages/migration/web.adoc

@@ -42,21 +42,57 @@ This will tell the Spring Security DSL to use `PathPatternRequestMatcher` for al
 
 In the event that you are directly constructing an object (as opposed to having the DSL construct it) that has a `setRequestMatcher` method. you should also proactively specify a `PathPatternRequestMatcher` there as well.
 
-For example, in the case of `LogoutFilter`, it constructs an `AntPathRequestMatcher` in Spring Security 6:
+=== Migrate `exitUserUrl` and `switchUserUrl` Request Matchers in `SwitchUserFilter`
 
-[method,java]
+`SwitchUserFilter`, constructs an `AntPathRequestMatcher` in its `setExitUserUrl` and `setSwitchUserUrl` methods.
+This will change to use `PathPatternRequestMatcher` in Spring Security 7.
+
+To prepare for this change, call `setExitUserMatcher` and `setSwithcUserMatcher` to provide this `PathPatternRequestMatcher` in advance.
+That is, change this:
+
+[tabs]
+======
+Java::
++
+[source,java,role="primary"]
 ----
-private RequestMatcher logoutUrl = new AntPathRequestMatcher("/logout");
+SwitchUserFilter switchUser = new SwitchUserFilter();
+// ... other configuration
+switchUser.setExitUserUrl("/exit/impersonate");
 ----
 
-and will change this to a `PathPatternRequestMatcher` in 7:
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+val switchUser = SwitchUserFilter()
+// ... other configuration
+switchUser.setExitUserUrl("/exit/impersonate")
+----
+======
 
-[method,java]
+to this:
+
+[tabs]
+======
+Java::
++
+[source,java,role="primary"]
 ----
-private RequestMatcher logoutUrl = PathPatternRequestMatcher.path().matcher("/logout");
+SwitchUserFilter switchUser = new SwitchUserFilter();
+// ... other configuration
+switchUser.setExitUserMatcher(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, "/exit/impersonate"));
 ----
 
-If you are constructing your own `LogoutFilter`, consider calling `setLogoutRequestMatcher` to provide this `PathPatternRequestMatcher` in advance.
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+val switchUser = SwitchUserFilter()
+// ... other configuration
+switchUser.setExitUserMatcher(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, "/exit/impersonate"))
+----
+======
 
 == Include the Servlet Path Prefix in Authorization Rules
 

+ 10 - 5
web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java

@@ -35,6 +35,7 @@ import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceAware;
 import org.springframework.context.support.MessageSourceAccessor;
 import org.springframework.core.log.LogMessage;
+import org.springframework.http.HttpMethod;
 import org.springframework.security.authentication.AccountExpiredException;
 import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
@@ -62,6 +63,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
 import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
 import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
 import org.springframework.security.web.context.SecurityContextRepository;
+import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
 import org.springframework.security.web.util.UrlUtils;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -127,9 +129,9 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
 
 	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
 
-	private RequestMatcher exitUserMatcher = createMatcher("/logout/impersonate");
+	private RequestMatcher exitUserMatcher = createMatcher("/logout/impersonate", true);
 
-	private RequestMatcher switchUserMatcher = createMatcher("/login/impersonate");
+	private RequestMatcher switchUserMatcher = createMatcher("/login/impersonate", true);
 
 	private String targetUrl;
 
@@ -406,7 +408,7 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
 	public void setExitUserUrl(String exitUserUrl) {
 		Assert.isTrue(UrlUtils.isValidRedirectUrl(exitUserUrl),
 				"exitUserUrl cannot be empty and must be a valid redirect URL");
-		this.exitUserMatcher = createMatcher(exitUserUrl);
+		this.exitUserMatcher = createMatcher(exitUserUrl, false);
 	}
 
 	/**
@@ -426,7 +428,7 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
 	public void setSwitchUserUrl(String switchUserUrl) {
 		Assert.isTrue(UrlUtils.isValidRedirectUrl(switchUserUrl),
 				"switchUserUrl cannot be empty and must be a valid redirect URL");
-		this.switchUserMatcher = createMatcher(switchUserUrl);
+		this.switchUserMatcher = createMatcher(switchUserUrl, false);
 	}
 
 	/**
@@ -545,7 +547,10 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
 		this.securityContextRepository = securityContextRepository;
 	}
 
-	private static RequestMatcher createMatcher(String pattern) {
+	private static RequestMatcher createMatcher(String pattern, boolean usePathPatterns) {
+		if (usePathPatterns) {
+			return PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, pattern);
+		}
 		return new AntPathRequestMatcher(pattern, "POST", true, new UrlPathHelper());
 	}