java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > @PreAuthorize注解源码

Java中的@PreAuthorize注解源码解析

作者:Sterne_

这篇文章主要介绍了Java中的@PreAuthorize注解源码解析,@PreAuthorize注解会在方法执行前进行权限验证,支持Spring EL表达式,它是基于方法注解的权限解决方案,需要的朋友可以参考下

一、PrePostAdviceReactiveMethodInterceptor类

作用

拦截@PreAuthorize注解标记的方法。

源码分析

// 源码存在删减
public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor {
	private Authentication anonymous = new AnonymousAuthenticationToken("key", "anonymous",
	private final MethodSecurityMetadataSource attributeSource;
	private final PreInvocationAuthorizationAdvice preInvocationAdvice;
	private final PostInvocationAuthorizationAdvice postAdvice;
	public PrePostAdviceReactiveMethodInterceptor(MethodSecurityMetadataSource attributeSource,
			PreInvocationAuthorizationAdvice preInvocationAdvice,
			PostInvocationAuthorizationAdvice postInvocationAdvice) {
		// attributeSource->PrePostAnnotationSecurityMetadataSource类,下文有相关解析
		this.attributeSource = attributeSource;
		// preInvocationAdvice->ExpressionBasedPreInvocationAdvice类,下文有相关解析
		this.preInvocationAdvice = preInvocationAdvice;
		this.postAdvice = postInvocationAdvice;
	}
    @Override
	public Object invoke(final MethodInvocation invocation) {
		Method method = invocation.getMethod();
		Class<?> returnType = method.getReturnType();
		Class<?> targetClass = invocation.getThis().getClass();
		// 关键步骤1,获取当前方法的安全属性集合,该方法解析在目录标题二
		Collection<ConfigAttribute> attributes = this.attributeSource.getAttributes(method, targetClass);
		// 关键步骤2:获取@PreAuthorize注解的value值
		PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
		Mono<Authentication> toInvoke = ReactiveSecurityContextHolder.getContext() // Mono<SecurityContext>
				.map(SecurityContext::getAuthentication)// Mono<Authentication>
				.defaultIfEmpty(this.anonymous)
				// 关键步骤3:调用ExpressionBasedPreInvocationAdvice类中的before方法,filter结果为true则保留元素,为false则删除元素
				.filter((auth) -> this.preInvocationAdvice.before(auth, invocation, preAttr))
				.switchIfEmpty(Mono.defer(() -> Mono.error(new AccessDeniedException("Denied"))));
}

对关键步骤3进行补充说明:

二、PrePostAnnotationSecurityMetadataSource类

类的继承关系

PrePostAnnotationSecuirtyMetadataSource继承实现类

作用

源码分析

// 获取@PreAuthorize相关源码部分展示
public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
	private final PrePostInvocationAttributeFactory attributeFactory;
	public PrePostAnnotationSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {
		this.attributeFactory = attributeFactory;
	}
	// PrePostAdviceReactiveMethodInterceptor invoke方法中调用该方法获取attributes
	@Override
	public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
		if (method.getDeclaringClass() == Object.class) {
			return Collections.emptyList();
		}
		PreAuthorize preAuthorize = findAnnotation(method, targetClass, PreAuthorize.class);
		if (preFilter == null && preAuthorize == null && postFilter == null && postAuthorize == null) {
			// There is no meta-data so return
			return Collections.emptyList();
		}
		String filterObject = (preFilter != null) ? preFilter.filterTarget() : null;
		// 获取@PreAuthorize注解的表达式
		String preAuthorizeAttribute = (preAuthorize != null) ? preAuthorize.value() : null;
		ArrayList<ConfigAttribute> attrs = new ArrayList<>(2);
		// 关键步骤1:创建PreAuthorize对应的ConfigAttribute
		PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(preFilterAttribute,
				filterObject, preAuthorizeAttribute);
		if (pre != null) {
			attrs.add(pre);
		}
		// 将容器的容量调整为当前元素的数量
		attrs.trimToSize();
		return attrs;
	}
}
// 解析注解中的表达式,创建相应的注解属性对象
public class ExpressionBasedAnnotationAttributeFactory implements PrePostInvocationAttributeFactory {
	private final Object parserLock = new Object();
	private ExpressionParser parser;
	// 对应下方代码的DefaultMethodSecurityExpressionHandler
	private MethodSecurityExpressionHandler handler;
	public ExpressionBasedAnnotationAttributeFactory(MethodSecurityExpressionHandler handler) {
		this.handler = handler;
	}
    // param: preAuthorizeAttribute 获取到的@PreAuthorize注解的表达式
	@Override
	public PreInvocationAttribute createPreInvocationAttribute(String preFilterAttribute, String filterObject,
			String preAuthorizeAttribute) {
		try {
		    // SpEL表达式解析器
			ExpressionParser parser = getParser();
			// 关键步骤
			Expression preAuthorizeExpression = (preAuthorizeAttribute != null)
					? parser.parseExpression(preAuthorizeAttribute) : parser.parseExpression("permitAll");
			Expression preFilterExpression = (preFilterAttribute != null) ? parser.parseExpression(preFilterAttribute)
					: null;
			// 关键步骤
			return new 
		PreInvocationExpressionAttribute(preFilterExpression, filterObject, preAuthorizeExpression);
		}
		catch (ParseException ex) {
			throw new IllegalArgumentException("Failed to parse expression '" + ex.getExpressionString() + "'", ex);
		}
	}
}

三、ExpressionBasedPreInvocationAdvice类

作用

解析@PreAuthorize中的SpEL表达式

源码分析

// 源码存在部分删减,仅展示分析与@PreAuthorize相关的内容
public class ExpressionBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice {
    // 关键类 第四点有对该类的关键方法进行解析
	private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
	@Override
	public boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute attr) {
		PreInvocationExpressionAttribute preAttr = (PreInvocationExpressionAttribute) attr;
		// 关键步骤 创建SpEL解析上下文
		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);
		Expression preAuthorize = preAttr.getAuthorizeExpression();
		// 关键步骤 计算表达式值
		return (preAuthorize != null) ? 
		ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx) : true;
	}
}

ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx方法补充说明:

根据提供的安全表达式和评估上下文 ctx 来评估安全表达式的结果,并返回一个布尔值。true,则权限校验通过;false,则校验失败。

四、DefaultMethodSecurityExpressionHandler类

作用

源码分析

public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>
		implements MethodSecurityExpressionHandler {
	// 用于处理表达式中的bean对象获取
	private BeanResolver beanResolver;
	private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
	// 这个类非常重要,下文会对这个类单独进行解析
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer();
	private PermissionCacheOptimizer permissionCacheOptimizer = null;
	private String defaultRolePrefix = "ROLE_";
	public DefaultMethodSecurityExpressionHandler() {
	}
	/**
	 * ExpressionBasedPreInvocationAdvice的before方法中调用该方法,创建方法安全表达式的评估上下文
	 */
	@Override
	public final EvaluationContext createEvaluationContext(Authentication authentication, T invocation) {
		SecurityExpressionOperations root = createSecurityExpressionRoot(authentication, invocation);
		StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
		ctx.setBeanResolver(this.beanResolver);
		ctx.setRootObject(root);
		return ctx;
	}
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.beanResolver = new BeanFactoryResolver(applicationContext);
	}
	/**
	 * 在 Spring Security 中,安全表达式用于在方法级别进行访问控制的决策。createEvaluationContextInternal方法在方法级别的安全表达式求值过程中被调用,其主要作用是创建一个评估上下文对象,以提供给安全表达式进行求值。
	 */
	@Override
	public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi) {
		return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
	}
	/**
	* 方法级别的安全表达式通常需要访问当前用户、目标对象和方法参数等相关     信息。createEvaluationContextInternal方法会使用 MethodSecurityExpressionRoot类的实例作为权限表达式的根对象,以便在表达式中访问这些信息。
	*/
	@Override
	protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
			MethodInvocation invocation) {
		MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
		root.setThis(invocation.getThis());
		root.setPermissionEvaluator(getPermissionEvaluator());
		root.setTrustResolver(getTrustResolver());
		root.setRoleHierarchy(getRoleHierarchy());
		root.setDefaultRolePrefix(getDefaultRolePrefix());
		return root;
	}
}

MethodSecurityExpressionOperations 类进行补充说明:

MethodSecurityExpressionOperations 接口定义了一组方法,用于在安全表达式中进行常见的操作和判断,例如获取当前用户信息、检查角色和权限等。下面举例该类的部分方法:

DefaultSecurityParameterNameDiscoverer 类进行补充说明: 在 Spring Security 中,当使用方法级别的注解(如 @PreAuthorize、@PostAuthorize、@PreFilter 和 @PostFilter)时,需要引用方法参数的名称来进行安全性评估和过滤操作。但编译器默认情况下不会在编译过程中保留方法参数的名称,而是使用类似 “arg0”、“arg1” 等默认名称。DefaultSecurityParameterNameDiscoverer 的作用就是解决这个问题,它通过不同的策略来发现方法参数的名称,以便在安全性注解中引用正确的参数。

到此这篇关于Java中的@PreAuthorize注解源码解析的文章就介绍到这了,更多相关@PreAuthorize注解源码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文