国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Spring Security源碼解析之權限訪(fǎng)問(wèn)控制是如何做到

發(fā)布時(shí)間:2021-07-05 18:40 來(lái)源:腳本之家 閱讀:0 作者:木兮同學(xué) 欄目: 開(kāi)發(fā)技術(shù)

目錄

    〇、前文回顧

    在實(shí)戰篇《》我們學(xué)習了Spring Security強大的訪(fǎng)問(wèn)控制能力,只需要進(jìn)行寥寥幾行的配置就能做到權限的控制,本篇來(lái)看看它到底是如何做到的。


    一、再聊過(guò)濾器鏈

    源碼篇中反復提到,請求進(jìn)來(lái)需要經(jīng)過(guò)的是一堆過(guò)濾器形成的過(guò)濾器鏈,走完過(guò)濾器鏈未拋出異常則可以繼續訪(fǎng)問(wèn)后臺接口資源,而最后一個(gè)過(guò)濾器就是來(lái)判斷請求是否有權限繼續訪(fǎng)問(wèn)后臺資源,如果沒(méi)有則會(huì )將拒絕訪(fǎng)問(wèn)的異常往上向異常過(guò)濾器拋,異常過(guò)濾器會(huì )對異常進(jìn)行翻譯,然后響應給客戶(hù)端。

    所以,一般情況下最后一個(gè)過(guò)濾器是做權限訪(fǎng)問(wèn)控制的核心過(guò)濾器FilterSecurityInterceptor ,而倒數第二個(gè)是異常翻譯過(guò)濾器ExceptionTranslationFilter ,將異常進(jìn)行翻譯然后響應給客戶(hù)端。比如我們實(shí)戰項目過(guò)濾器鏈圖解


    二、過(guò)濾器的創(chuàng )建

    FilterSecurityInterceptor的創(chuàng )建

    這個(gè)過(guò)濾器的配置器是 ExpressionUrlAuthorizationConfigurer ,它的父類(lèi) AbstractInterceptUrlConfigurer 中的 configure() 方法創(chuàng )建了這個(gè)過(guò)濾器。

    abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
    		extends AbstractHttpConfigurer<C, H> {
    	...
    	@Override
    	public void configure(H http) throws Exception {
    		FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
    		if (metadataSource == null) {
    			return;
    		}
    		FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
    				http, metadataSource, http.getSharedObject(AuthenticationManager.class));
    		if (filterSecurityInterceptorOncePerRequest != null) {
    			securityInterceptor
    					.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
    		}
    		securityInterceptor = postProcess(securityInterceptor);
    		http.addFilter(securityInterceptor);
    		http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
    	}
    	...
    }

    這個(gè)過(guò)濾器的配置器是在 HttpSecurityauthorizeRequests() 方法中apply進(jìn)來(lái)的,在我們自己配置的核心配置器中使用的就是該種基于 HttpServletRequest 限制訪(fǎng)問(wèn)的方式。

    ExceptionTranslationFilter的創(chuàng )建

    這個(gè)過(guò)濾器的配置器是 ExceptionHandlingConfigurer ,它自己的 configure() 方法中創(chuàng )建了這個(gè)過(guò)濾器。

    public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> extends
    		AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>, H> {
    	...
    	@Override
    	public void configure(H http) throws Exception {
    		AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
    		ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
    				entryPoint, getRequestCache(http));
    		if (accessDeniedHandler != null) {
    			exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
    		}
    		exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
    		http.addFilter(exceptionTranslationFilter);
    	}
    	...
    }

    這個(gè)過(guò)濾器的配置器是在 HttpSecurityexceptionHandling() 方法中apply進(jìn)來(lái)的,和上面不同的是,這個(gè)過(guò)濾器配置器會(huì )默認被apply進(jìn) HttpSecurity,在 WebSecurityConfigurerAdapter 中的 init() 方法,里面調用了 getHttp() 方法,這里定義了很多默認的過(guò)濾器配置,其中就包括當前過(guò)濾器配置。


    三、源碼流程

    FilterSecurityInterceptor

    • 進(jìn)入:doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    • 進(jìn)入:invoke(FilterInvocation fi)
    • 進(jìn)入:beforeInvocation(Object object)

    這個(gè)方法里面有個(gè) attributes ,里面獲取的就是當前request請求所能匹配中的權限Spel表達式,比如這里是 hasRole('ROLE_BUYER')
    方法源碼如下,繼續往下走

    protected InterceptorStatusToken beforeInvocation(Object object) {
    		
    		...
    
    		// 獲取當前request請求所能匹配中的權限Spel表達式
    		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
    				.getAttributes(object);
    				
    		...
    
    		// Attempt authorization
    		try {
    			this.accessDecisionManager.decide(authenticated, object, attributes);
    		}
    		catch (AccessDeniedException accessDeniedException) {
    			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
    					accessDeniedException));
    
    			throw accessDeniedException;
    		}
    		
    		...
    		
    	}

    進(jìn)入:decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)

    這里有個(gè)投票器,投票結果為1表示可以訪(fǎng)問(wèn)直接返回,投票結果為-1表示拒絕訪(fǎng)問(wèn),向上拋拒絕訪(fǎng)問(wèn)異常,這里使用的投票器是 WebExpressionVoter

    public void decide(Authentication authentication, Object object,
    			Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
    		int deny = 0;
    
    		for (AccessDecisionVoter voter : getDecisionVoters()) {
    			int result = voter.vote(authentication, object, configAttributes);
    
    			if (logger.isDebugEnabled()) {
    				logger.debug("Voter: " + voter + ", returned: " + result);
    			}
    
    			switch (result) {
    			case AccessDecisionVoter.ACCESS_GRANTED:
    				return;
    
    			case AccessDecisionVoter.ACCESS_DENIED:
    				deny++;
    
    				break;
    
    			default:
    				break;
    			}
    		}
    
    		if (deny > 0) {
    			throw new AccessDeniedException(messages.getMessage(
    					"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
    		}
    
    		// To get this far, every AccessDecisionVoter abstained
    		checkAllowIfAllAbstainDecisions();
    	}

    進(jìn)入:vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes)

    這里面其實(shí)就是使用Spring的Spel表達式進(jìn)行投票,使用請求中的權限表達式組裝Expression,使用Token令牌中的權限組裝EvaluationContext,然后調用 ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx),

    public int vote(Authentication authentication, FilterInvocation fi,
    			Collection<ConfigAttribute> attributes) {
    		assert authentication != null;
    		assert fi != null;
    		assert attributes != null;
    
    		WebExpressionConfigAttribute weca = findConfigAttribute(attributes);
    
    		if (weca == null) {
    			return ACCESS_ABSTAIN;
    		}
    
    		EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication,
    				fi);
    		ctx = weca.postProcess(ctx, fi);
    
    		return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED
    				: ACCESS_DENIED;
    	}

    evaluateAsBoolean() 方法里面就是調用Expression的 getValue() 方法,獲取實(shí)際的匹配結果,如下圖Spel表達式為 hasRole('ROLE_BUYER')

    所以它實(shí)際調用的是 SecurityExpressionRoot#hasRole 方法(關(guān)于權限表達式對應實(shí)際調用的方法,在《手把手教你如何使用Spring Security(下):訪(fǎng)問(wèn)控制》文章中已貼出,下面文章也補充一份),里面的邏輯其實(shí)就是判斷Token令牌中是否包含有 ROLE_BUYER 的角色,有的話(huà)返回true,否則返回false,如下為 SecurityExpressionRoot#hasRole 方法源碼:

    private boolean hasAnyAuthorityName(String prefix, String... roles) {
    		Set<String> roleSet = getAuthoritySet();
    
    		for (String role : roles) {
    			String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
    			if (roleSet.contains(defaultedRole)) {
    				return true;
    			}
    		}
    
    		return false;
    	}
    • 如果投票成功,則會(huì )一直返回到 invoke() 方法,再執行后續過(guò)濾器,未拋異常表示該請求已經(jīng)有訪(fǎng)問(wèn)權限了
    • 假如投票失敗,在 decide() 方法中會(huì )向上拋拒絕訪(fǎng)問(wèn)異常,一直往上拋直到被處理,往上反向跟蹤發(fā)現這個(gè)過(guò)濾器一直沒(méi)有處理拒絕訪(fǎng)問(wèn)異常,那就繼續往上個(gè)過(guò)濾器拋,就到了我們的異常翻譯過(guò)濾器 ExceptionTranslationFilter。

    ExceptionTranslationFilter

    該過(guò)濾器的 doFilter() 方法很簡(jiǎn)單,沒(méi)有邏輯處理,只對后續過(guò)濾器拋出的異常進(jìn)行處理,源碼如下:

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
    			throws IOException, ServletException {
    		HttpServletRequest request = (HttpServletRequest) req;
    		HttpServletResponse response = (HttpServletResponse) res;
    
    		try {
    			chain.doFilter(request, response);
    
    			logger.debug("Chain processed normally");
    		}
    		catch (IOException ex) {
    			throw ex;
    		}
    		catch (Exception ex) {
    			// Try to extract a SpringSecurityException from the stacktrace
    			Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
    			RuntimeException ase = (AuthenticationException) throwableAnalyzer
    					.getFirstThrowableOfType(AuthenticationException.class, causeChain);
    
    			if (ase == null) {
    				ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
    						AccessDeniedException.class, causeChain);
    			}
    
    			if (ase != null) {
    				handleSpringSecurityException(request, response, chain, ase);
    			}
    			else {
    				// Rethrow ServletExceptions and RuntimeExceptions as-is
    				if (ex instanceof ServletException) {
    					throw (ServletException) ex;
    				}
    				else if (ex instanceof RuntimeException) {
    					throw (RuntimeException) ex;
    				}
    
    				// Wrap other Exceptions. This shouldn't actually happen
    				// as we've already covered all the possibilities for doFilter
    				throw new RuntimeException(ex);
    			}
    		}
    	}

    當拋出拒絕訪(fǎng)問(wèn)異常后,繼續調用 handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) 方法,方法里面主要將異常信息和錯誤碼設置到響應頭,然后響應到客戶(hù)端,請求結束。

    補充:權限表達式

    四、總結

    • 訪(fǎng)問(wèn)控制的核心過(guò)濾器是 FilterSecurityInterceptor ,當然這個(gè)是可選的,我們完全也可以自定義一個(gè)過(guò)濾器去處理權限訪(fǎng)問(wèn)。
    • 處理訪(fǎng)問(wèn)異常處理的過(guò)濾器是 ExceptionTranslationFilter ,里面邏輯很簡(jiǎn)單,給response設置異常信息錯誤碼,再返回給客戶(hù)端。

    以上就是Spring Security源碼解析之權限訪(fǎng)問(wèn)控制是如何做到的的詳細內容,更多關(guān)于Spring Security權限訪(fǎng)問(wèn)控制的資料請關(guān)注腳本之家其它相關(guān)文章!

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

    超碰CAO已满18进入离开| 国产精品无码素人福利| 人妻无码中文字幕一区二区三区| 国产偷窥熟女精品视频大全| 一区二区三区无码免费看| 国产精品原创AV片国产日韩|