Sfoglia il codice sorgente

Add Request Path Extraction Support

Closes gh-13256
Taehong Kim 2 anni fa
parent
commit
ec02c22459

+ 52 - 0
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.ObjectPostProcessor;
 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 final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
 			return access(AuthenticatedAuthorizationManager.anonymous());
 		}
 
+		/**
+		 * Specify that a path variable in URL to be compared.
+		 *
+		 * <p>
+		 * For example, <pre>
+		 * requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName)
+		 * </pre>
+		 * @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 final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
 					: 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
+			 * <p>
+			 * For example, <pre>
+			 * requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName));
+			 * </pre>
+			 * @param function a function to get value from {@link Authentication}.
+			 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+			 * customization.
+			 */
+			public AuthorizationManagerRequestMatcherRegistry equalTo(Function<Authentication, String> function) {
+				return access((auth, requestContext) -> {
+					String value = requestContext.getVariables().get(this.variable);
+					return new AuthorizationDecision(function.apply(auth.get()).equals(value));
+				});
+			}
+
+		}
+
 	}
 
 }

+ 19 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java

@@ -40,8 +40,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 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 class AuthorizeHttpRequestsConfigurerTests {
 		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 @@ public class AuthorizeHttpRequestsConfigurerTests {
 				.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 @@ public class AuthorizeHttpRequestsConfigurerTests {
 				return username;
 			}
 
+			@RequestMapping("/v2/user/{username}")
+			String pathV2(@PathVariable("username") String username) {
+				return username;
+			}
+
 		}
 
 	}

+ 3 - 3
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());
 	}