diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
index 7937143b318..82b2d329940 100644
--- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
+++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
@@ -17,6 +17,7 @@
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
+import java.util.function.Function;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
@@ -37,6 +38,7 @@
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
+import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
@@ -387,6 +389,21 @@ public AuthorizationManagerRequestMatcherRegistry anonymous() {
return access(AuthenticatedAuthorizationManager.anonymous());
}
+ /**
+ * Specify that a path variable in URL to be compared.
+ *
+ *
+ * For example,
+ * requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName)
+ *
+ * @param variable the variable in URL template to compare.
+ * @return {@link AuthorizedUrlVariable} for further customization.
+ * @since 6.3
+ */
+ public AuthorizedUrlVariable hasVariable(String variable) {
+ return new AuthorizedUrlVariable(variable);
+ }
+
/**
* Allows specifying a custom {@link AuthorizationManager}.
* @param manager the {@link AuthorizationManager} to use
@@ -401,6 +418,41 @@ public AuthorizationManagerRequestMatcherRegistry access(
: AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
}
+ /**
+ * An object that allows configuring {@link RequestMatcher}s with URI path
+ * variables
+ *
+ * @author Taehong Kim
+ * @since 6.3
+ */
+ public final class AuthorizedUrlVariable {
+
+ private final String variable;
+
+ private AuthorizedUrlVariable(String variable) {
+ this.variable = variable;
+ }
+
+ /**
+ * Compares the value of a path variable in the URI with an `Authentication`
+ * attribute
+ *
+ * For example,
+ * requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName));
+ *
+ * @param function a function to get value from {@link Authentication}.
+ * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+ * customization.
+ */
+ public AuthorizationManagerRequestMatcherRegistry equalTo(Function function) {
+ return access((auth, requestContext) -> {
+ String value = requestContext.getVariables().get(this.variable);
+ return new AuthorizationDecision(function.apply(auth.get()).equals(value));
+ });
+ }
+
+ }
+
}
}
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java
index 5f220a7a177..0aad4d777ca 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java
@@ -40,8 +40,10 @@
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
+import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@@ -543,6 +545,17 @@ public void requestWhenMvcMatcherPathVariablesThenMatchesOnPathVariables() throw
this.mvc.perform(request).andExpect(status().isOk());
request = get("/user/deny");
this.mvc.perform(request).andExpect(status().isUnauthorized());
+
+ UserDetails user = TestAuthentication.withUsername("taehong").build();
+ Authentication authentication = TestAuthentication.authenticated(user);
+ request = get("/v2/user/{username}", user.getUsername()).with(authentication(authentication));
+ this.mvc.perform(request).andExpect(status().isOk());
+
+ request = get("/v2/user/{username}", "withNoAuthentication");
+ this.mvc.perform(request).andExpect(status().isUnauthorized());
+
+ request = get("/v2/user/{username}", "another").with(authentication(authentication));
+ this.mvc.perform(request).andExpect(status().isForbidden());
}
private static RequestPostProcessor remoteAddress(String remoteAddress) {
@@ -1067,6 +1080,7 @@ SecurityFilterChain chain(HttpSecurity http) throws Exception {
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/user/{username}").access(new WebExpressionAuthorizationManager("#username == 'user'"))
+ .requestMatchers("/v2/user/{username}").hasVariable("username").equalTo(Authentication::getName)
);
// @formatter:on
return http.build();
@@ -1080,6 +1094,11 @@ String path(@PathVariable("username") String username) {
return username;
}
+ @RequestMapping("/v2/user/{username}")
+ String pathV2(@PathVariable("username") String username) {
+ return username;
+ }
+
}
}
diff --git a/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java b/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java
index a0faebee71c..51c81e9626c 100644
--- a/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java
+++ b/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java
@@ -35,14 +35,14 @@ public class TestAuthentication extends PasswordEncodedUser {
AuthorityUtils.createAuthorityList("ROLE_USER"));
public static Authentication authenticatedAdmin() {
- return autheticated(admin());
+ return authenticated(admin());
}
public static Authentication authenticatedUser() {
- return autheticated(user());
+ return authenticated(user());
}
- public static Authentication autheticated(UserDetails user) {
+ public static Authentication authenticated(UserDetails user) {
return UsernamePasswordAuthenticationToken.authenticated(user, null, user.getAuthorities());
}