mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 06:38:42 +00:00 
			
		
		
		
	Add WebExpressionAuthorizationManager.Builder
Closes gh-17504
This commit is contained in:
		
							parent
							
								
									c312d18191
								
							
						
					
					
						commit
						dadf10899c
					
				| @ -996,6 +996,29 @@ Kotlin:: | |||||||
| ---- | ---- | ||||||
| ====== | ====== | ||||||
| 
 | 
 | ||||||
|  | To migrate several, you can use `WebExpressionAuthorizationManager#withDefaults`: | ||||||
|  | 
 | ||||||
|  | [tabs] | ||||||
|  | ====== | ||||||
|  | Java:: | ||||||
|  | + | ||||||
|  | [source,java,role="primary"] | ||||||
|  | ---- | ||||||
|  | WebExpressionAuthorizationManager.Builder authz = WebExpressionAuthorizationManager.withDefaults(); | ||||||
|  | .requestMatchers("/test/**").access(authz.expression("hasRole('ADMIN') && hasRole('USER')")) | ||||||
|  | .requestMatchers("/test/**").access(authz.expression("permitAll")) | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | Kotlin:: | ||||||
|  | + | ||||||
|  | [source,kotlin,role="secondary"] | ||||||
|  | ---- | ||||||
|  | var authz = WebExpressionAuthorizationManager.withDefaults() | ||||||
|  | .requestMatchers("/test/**").access(authz.expression("hasRole('ADMIN') && hasRole('USER')")) | ||||||
|  | .requestMatchers("/test/**").access(authz.expression("permitAll")) | ||||||
|  | ---- | ||||||
|  | ====== | ||||||
|  | 
 | ||||||
| If you are referring to a bean in your expression like so: `@webSecurity.check(authentication, request)`, it's recommended that you instead call the bean directly, which will look something like the following: | If you are referring to a bean in your expression like so: `@webSecurity.check(authentication, request)`, it's recommended that you instead call the bean directly, which will look something like the following: | ||||||
| 
 | 
 | ||||||
| [tabs] | [tabs] | ||||||
| @ -1019,7 +1042,32 @@ Kotlin:: | |||||||
| 
 | 
 | ||||||
| For complex instructions that include bean references as well as other expressions, it is recommended that you change those to implement `AuthorizationManager` and refer to them by calling `.access(AuthorizationManager)`. | For complex instructions that include bean references as well as other expressions, it is recommended that you change those to implement `AuthorizationManager` and refer to them by calling `.access(AuthorizationManager)`. | ||||||
| 
 | 
 | ||||||
| If you are not able to do that, you can configure a javadoc:org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler[] with a bean resolver and supply that to `WebExpressionAuthorizationManager#setExpressionhandler`. | If you are not able to do that, you can publish javadoc:org.springframework.security.web.access.expression.WebExpressionAuthorizationManager$Builder[] as a bean: | ||||||
|  | 
 | ||||||
|  | [tabs] | ||||||
|  | ====== | ||||||
|  | Java:: | ||||||
|  | + | ||||||
|  | [source,java,role="primary"] | ||||||
|  | ---- | ||||||
|  | @Bean | ||||||
|  | WebExpressionAuthorizationManager.Builder authz() { | ||||||
|  | 	return WebExpressionAuthorizationManager.withDefaults(); | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | Kotlin:: | ||||||
|  | + | ||||||
|  | [source,kotlin,role="secondary"] | ||||||
|  | ---- | ||||||
|  | @Bean | ||||||
|  | fun authz(): WebExpressionAuthorizationManager.Builder { | ||||||
|  | 	return WebExpressionAuthorizationManager.withDefaults() | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  | ====== | ||||||
|  | 
 | ||||||
|  | Then, expressions passed to that builder will be able to refer to beans. | ||||||
| 
 | 
 | ||||||
| [[security-matchers]] | [[security-matchers]] | ||||||
| == Security Matchers | == Security Matchers | ||||||
|  | |||||||
| @ -18,6 +18,9 @@ package org.springframework.security.web.access.expression; | |||||||
| 
 | 
 | ||||||
| import java.util.function.Supplier; | import java.util.function.Supplier; | ||||||
| 
 | 
 | ||||||
|  | import org.springframework.beans.BeansException; | ||||||
|  | import org.springframework.context.ApplicationContext; | ||||||
|  | import org.springframework.context.ApplicationContextAware; | ||||||
| import org.springframework.expression.EvaluationContext; | import org.springframework.expression.EvaluationContext; | ||||||
| import org.springframework.expression.Expression; | import org.springframework.expression.Expression; | ||||||
| import org.springframework.security.access.expression.ExpressionUtils; | import org.springframework.security.access.expression.ExpressionUtils; | ||||||
| @ -51,11 +54,20 @@ public final class WebExpressionAuthorizationManager implements AuthorizationMan | |||||||
| 		this.expression = this.expressionHandler.getExpressionParser().parseExpression(expressionString); | 		this.expression = this.expressionHandler.getExpressionParser().parseExpression(expressionString); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	private WebExpressionAuthorizationManager(String expressionString, | ||||||
|  | 			SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) { | ||||||
|  | 		Assert.hasText(expressionString, "expressionString cannot be empty"); | ||||||
|  | 		this.expressionHandler = expressionHandler; | ||||||
|  | 		this.expression = expressionHandler.getExpressionParser().parseExpression(expressionString); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Sets the {@link SecurityExpressionHandler} to be used. The default is | 	 * Sets the {@link SecurityExpressionHandler} to be used. The default is | ||||||
| 	 * {@link DefaultHttpSecurityExpressionHandler}. | 	 * {@link DefaultHttpSecurityExpressionHandler}. | ||||||
| 	 * @param expressionHandler the {@link SecurityExpressionHandler} to use | 	 * @param expressionHandler the {@link SecurityExpressionHandler} to use | ||||||
|  | 	 * @deprecated Please use {@link #withDefaults()} or {@link #withExpressionHandler} | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@Deprecated | ||||||
| 	public void setExpressionHandler(SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) { | 	public void setExpressionHandler(SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) { | ||||||
| 		Assert.notNull(expressionHandler, "expressionHandler cannot be null"); | 		Assert.notNull(expressionHandler, "expressionHandler cannot be null"); | ||||||
| 		this.expressionHandler = expressionHandler; | 		this.expressionHandler = expressionHandler; | ||||||
| @ -82,4 +94,78 @@ public final class WebExpressionAuthorizationManager implements AuthorizationMan | |||||||
| 		return "WebExpressionAuthorizationManager[expression='" + this.expression + "']"; | 		return "WebExpressionAuthorizationManager[expression='" + this.expression + "']"; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Use a {@link DefaultHttpSecurityExpressionHandler} to create | ||||||
|  | 	 * {@link WebExpressionAuthorizationManager} instances. | ||||||
|  | 	 * | ||||||
|  | 	 * <p> | ||||||
|  | 	 * Note that publishing the {@link Builder} as a bean will allow the default | ||||||
|  | 	 * expression handler to be configured with a bean provider so that expressions can | ||||||
|  | 	 * reference beans | ||||||
|  | 	 * @return a {@link Builder} for constructing | ||||||
|  | 	 * {@link WebExpressionAuthorizationManager} instances | ||||||
|  | 	 * @since 7.0 | ||||||
|  | 	 */ | ||||||
|  | 	public static Builder withDefaults() { | ||||||
|  | 		return new Builder(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Use this {@link SecurityExpressionHandler} to create | ||||||
|  | 	 * {@link WebExpressionAuthorizationManager} instances | ||||||
|  | 	 * @param expressionHandler | ||||||
|  | 	 * @return a {@link Builder} for constructing | ||||||
|  | 	 * {@link WebExpressionAuthorizationManager} instances | ||||||
|  | 	 * @since 7.0 | ||||||
|  | 	 */ | ||||||
|  | 	public static Builder withExpressionHandler( | ||||||
|  | 			SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) { | ||||||
|  | 		return new Builder(expressionHandler); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * A {@link Builder} for constructing {@link WebExpressionAuthorizationManager} | ||||||
|  | 	 * instances. | ||||||
|  | 	 * | ||||||
|  | 	 * <p> | ||||||
|  | 	 * May be reused to create multiple instances. | ||||||
|  | 	 * | ||||||
|  | 	 * @author Josh Cummings | ||||||
|  | 	 * @since 7.0 | ||||||
|  | 	 */ | ||||||
|  | 	public static final class Builder implements ApplicationContextAware { | ||||||
|  | 
 | ||||||
|  | 		private final SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler; | ||||||
|  | 
 | ||||||
|  | 		private final boolean defaultExpressionHandler; | ||||||
|  | 
 | ||||||
|  | 		private Builder() { | ||||||
|  | 			this.expressionHandler = new DefaultHttpSecurityExpressionHandler(); | ||||||
|  | 			this.defaultExpressionHandler = true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private Builder(SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) { | ||||||
|  | 			this.expressionHandler = expressionHandler; | ||||||
|  | 			this.defaultExpressionHandler = false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * Create a {@link WebExpressionAuthorizationManager} using this | ||||||
|  | 		 * {@code expression} | ||||||
|  | 		 * @param expression the expression to evaluate | ||||||
|  | 		 * @return the resulting {@link AuthorizationManager} | ||||||
|  | 		 */ | ||||||
|  | 		public WebExpressionAuthorizationManager expression(String expression) { | ||||||
|  | 			return new WebExpressionAuthorizationManager(expression, this.expressionHandler); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void setApplicationContext(ApplicationContext context) throws BeansException { | ||||||
|  | 			if (this.defaultExpressionHandler) { | ||||||
|  | 				((DefaultHttpSecurityExpressionHandler) this.expressionHandler).setApplicationContext(context); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ package org.springframework.security.web.access.expression; | |||||||
| 
 | 
 | ||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| 
 | 
 | ||||||
|  | import org.springframework.context.support.GenericApplicationContext; | ||||||
| import org.springframework.expression.Expression; | import org.springframework.expression.Expression; | ||||||
| import org.springframework.expression.ExpressionParser; | import org.springframework.expression.ExpressionParser; | ||||||
| import org.springframework.mock.web.MockHttpServletRequest; | import org.springframework.mock.web.MockHttpServletRequest; | ||||||
| @ -102,4 +103,49 @@ class WebExpressionAuthorizationManagerTests { | |||||||
| 		assertThat(decision.isGranted()).isFalse(); | 		assertThat(decision.isGranted()).isFalse(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void authorizeWhenDefaultsThenEvaluatesExpressionsReferencingBeans() { | ||||||
|  | 		GenericApplicationContext context = new GenericApplicationContext(); | ||||||
|  | 		context.registerBean("bean", WebExpressionAuthorizationManagerTests.class, () -> this); | ||||||
|  | 		context.refresh(); | ||||||
|  | 		WebExpressionAuthorizationManager.Builder builder = WebExpressionAuthorizationManager.withDefaults(); | ||||||
|  | 		builder.setApplicationContext(context); | ||||||
|  | 		WebExpressionAuthorizationManager manager = builder | ||||||
|  | 			.expression("@bean.class.simpleName.startsWith('WebExpression')"); | ||||||
|  | 		AuthorizationResult result = manager.authorize(TestAuthentication::authenticatedUser, | ||||||
|  | 				new RequestAuthorizationContext(new MockHttpServletRequest())); | ||||||
|  | 		assertThat(result.isGranted()).isTrue(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void authorizeWhenDefaultsAsBeanThenEvaluatesExpressionsReferencingBeans() { | ||||||
|  | 		GenericApplicationContext context = new GenericApplicationContext(); | ||||||
|  | 		context.registerBean("bean", WebExpressionAuthorizationManagerTests.class, () -> this); | ||||||
|  | 		context.registerBean("builder", WebExpressionAuthorizationManager.Builder.class, | ||||||
|  | 				WebExpressionAuthorizationManager::withDefaults); | ||||||
|  | 		context.refresh(); | ||||||
|  | 		WebExpressionAuthorizationManager.Builder builder = context | ||||||
|  | 			.getBean(WebExpressionAuthorizationManager.Builder.class); | ||||||
|  | 		WebExpressionAuthorizationManager manager = builder | ||||||
|  | 			.expression("@bean.class.simpleName.startsWith('WebExpression')"); | ||||||
|  | 		AuthorizationResult result = manager.authorize(TestAuthentication::authenticatedUser, | ||||||
|  | 				new RequestAuthorizationContext(new MockHttpServletRequest())); | ||||||
|  | 		assertThat(result.isGranted()).isTrue(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void authorizeWhenExpressionHandlerHasBeanProviderThenEvaluatesExpressionsReferencingBeans() { | ||||||
|  | 		GenericApplicationContext context = new GenericApplicationContext(); | ||||||
|  | 		context.registerBean("bean", WebExpressionAuthorizationManagerTests.class, () -> this); | ||||||
|  | 		context.refresh(); | ||||||
|  | 		DefaultHttpSecurityExpressionHandler expressionHandler = new DefaultHttpSecurityExpressionHandler(); | ||||||
|  | 		expressionHandler.setApplicationContext(context); | ||||||
|  | 		WebExpressionAuthorizationManager manager = WebExpressionAuthorizationManager | ||||||
|  | 			.withExpressionHandler(expressionHandler) | ||||||
|  | 			.expression("@bean.class.simpleName.startsWith('WebExpression')"); | ||||||
|  | 		AuthorizationResult result = manager.authorize(TestAuthentication::authenticatedUser, | ||||||
|  | 				new RequestAuthorizationContext(new MockHttpServletRequest())); | ||||||
|  | 		assertThat(result.isGranted()).isTrue(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user