最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的@PreAuthorize注解源碼解析

 更新時間:2023年10月07日 08:39:34   作者:Sterne_  
這篇文章主要介紹了Java中的@PreAuthorize注解源碼解析,@PreAuthorize注解會在方法執(zhí)行前進行權限驗證,支持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進行補充說明:

  • 當前的安全上下文中不存在認證信息(Authentication),即 ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication) 返回空的 Mono 對象。
  • 調用 preInvocationAdvice.before(auth, invocation, preAttr) 方法返回 false,即預授權邏輯拒絕了訪問請求。
  • 在這兩種情況下,都會使用 Mono.error(new AccessDeniedException(“Denied”)) 創(chuàng)建一個錯誤的 Mono 對象,并通過 switchIfEmpty 方法替換之前的空 Mono 對象,從而觸發(fā)異常并拋出 AccessDeniedException。

二、PrePostAnnotationSecurityMetadataSource類

類的繼承關系

PrePostAnnotationSecuirtyMetadataSource繼承實現(xiàn)類

作用

  • 解析注解:它解析方法上的PreAuthorize和PostAuthorize等注解,提取其中的權限表達式、角色信息等。
  • 提供權限驗證元數(shù)據(jù):根據(jù)解析得到的注解信息,PrePostAnnotationSecurityMetadataSource提供相應的權限驗證元數(shù)據(jù)。這些元數(shù)據(jù)通常是ConfigAttribute對象的集合,每個ConfigAttribute表示一個權限驗證的配置。
  • 支持方法級別的權限驗證:通過為方法提供權限驗證元數(shù)據(jù),PrePostAnnotationSecurityMetadataSource支持在方法級別對權限進行驗證。這使得開發(fā)者可以在方法執(zhí)行前后定義細粒度的權限控制邏輯。
  • 與其他組件配合使用:PrePostAnnotationSecurityMetadataSource通常與其他Spring Security的組件(如AccessDecisionManager、MethodSecurityInterceptor等)配合使用,以實現(xiàn)方法級別的權限驗證。

源碼分析

// 獲取@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:創(chuàng)建PreAuthorize對應的ConfigAttribute
		PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(preFilterAttribute,
				filterObject, preAuthorizeAttribute);
		if (pre != null) {
			attrs.add(pre);
		}
		// 將容器的容量調整為當前元素的數(shù)量
		attrs.trimToSize();
		return attrs;
	}
}
// 解析注解中的表達式,創(chuàng)建相應的注解屬性對象
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;
		// 關鍵步驟 創(chuàng)建SpEL解析上下文
		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);
		Expression preAuthorize = preAttr.getAuthorizeExpression();
		// 關鍵步驟 計算表達式值
		return (preAuthorize != null) ? 
		ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx) : true;
	}
}

ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx方法補充說明:

根據(jù)提供的安全表達式和評估上下文 ctx 來評估安全表達式的結果,并返回一個布爾值。true,則權限校驗通過;false,則校驗失敗。

四、DefaultMethodSecurityExpressionHandler類

作用

  • 創(chuàng)建評估上下文:在安全表達式求值之前,DefaultMethodSecurityExpressionHandler 會創(chuàng)建一個評估上下文EvaluationContext對象,以提供給安全表達式進行求值。評估上下文包含了當前用戶的身份驗證信息、目標對象和方法參數(shù)等相關信息。
  • 權限注解的處理:DefaultMethodSecurityExpressionHandler 支持處理方法參數(shù)上的權限注解,例如 @PreFilter 和 @PostFilter 注解。它會將這些注解解析為相應的安全表達式,并在評估上下文中傳遞方法參數(shù)的信息,以進行權限過濾操作。

源碼分析

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方法中調用該方法,創(chuàng)建方法安全表達式的評估上下文
	 */
	@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方法在方法級別的安全表達式求值過程中被調用,其主要作用是創(chuàng)建一個評估上下文對象,以提供給安全表達式進行求值。
	 */
	@Override
	public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi) {
		return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
	}
	/**
	* 方法級別的安全表達式通常需要訪問當前用戶、目標對象和方法參數(shù)等相關     信息。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 接口定義了一組方法,用于在安全表達式中進行常見的操作和判斷,例如獲取當前用戶信息、檢查角色和權限等。下面舉例該類的部分方法:

  • boolean hasAuthority(String authority)
  • boolean hasAnyAuthority(String… authorities)
  • boolean hasRole(String role)
  • boolean hasAnyRole(String… roles)
  • boolean permitAll()
  • boolean denyAll()
  • boolean hasPermission(Object target, Object permission)

DefaultSecurityParameterNameDiscoverer 類進行補充說明: 在 Spring Security 中,當使用方法級別的注解(如 @PreAuthorize、@PostAuthorize、@PreFilter 和 @PostFilter)時,需要引用方法參數(shù)的名稱來進行安全性評估和過濾操作。但編譯器默認情況下不會在編譯過程中保留方法參數(shù)的名稱,而是使用類似 “arg0”、“arg1” 等默認名稱。DefaultSecurityParameterNameDiscoverer 的作用就是解決這個問題,它通過不同的策略來發(fā)現(xiàn)方法參數(shù)的名稱,以便在安全性注解中引用正確的參數(shù)。

到此這篇關于Java中的@PreAuthorize注解源碼解析的文章就介紹到這了,更多相關@PreAuthorize注解源碼內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

南漳县| 陵川县| 禹州市| 咸宁市| 图们市| 太仓市| 辽阳县| 布尔津县| 黑河市| 故城县| 兰州市| 芮城县| 措美县| 沐川县| 南昌县| 运城市| 道孚县| 若尔盖县| 桐柏县| 洪江市| 铅山县| 皮山县| 隆昌县| 迁安市| 望城县| 西丰县| 鄄城县| 呼和浩特市| 固镇县| 和田市| 垦利县| 怀化市| 巫溪县| 莫力| 渝北区| 乌苏市| 桂阳县| 怀化市| 于田县| 桑日县| 金塔县|