mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +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: | ||||
| 
 | ||||
| [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)`. | ||||
| 
 | ||||
| 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 | ||||
|  | ||||
| @ -18,6 +18,9 @@ package org.springframework.security.web.access.expression; | ||||
| 
 | ||||
| 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.Expression; | ||||
| import org.springframework.security.access.expression.ExpressionUtils; | ||||
| @ -51,11 +54,20 @@ public final class WebExpressionAuthorizationManager implements AuthorizationMan | ||||
| 		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 | ||||
| 	 * {@link DefaultHttpSecurityExpressionHandler}. | ||||
| 	 * @param expressionHandler the {@link SecurityExpressionHandler} to use | ||||
| 	 * @deprecated Please use {@link #withDefaults()} or {@link #withExpressionHandler} | ||||
| 	 */ | ||||
| 	@Deprecated | ||||
| 	public void setExpressionHandler(SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) { | ||||
| 		Assert.notNull(expressionHandler, "expressionHandler cannot be null"); | ||||
| 		this.expressionHandler = expressionHandler; | ||||
| @ -82,4 +94,78 @@ public final class WebExpressionAuthorizationManager implements AuthorizationMan | ||||
| 		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.springframework.context.support.GenericApplicationContext; | ||||
| import org.springframework.expression.Expression; | ||||
| import org.springframework.expression.ExpressionParser; | ||||
| import org.springframework.mock.web.MockHttpServletRequest; | ||||
| @ -102,4 +103,49 @@ class WebExpressionAuthorizationManagerTests { | ||||
| 		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