溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Spring Security權(quán)限管理的投票器與表決機(jī)制怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2021-12-07 13:37:55 來(lái)源:億速云 閱讀:131 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“Spring Security權(quán)限管理的投票器與表決機(jī)制怎么實(shí)現(xiàn)”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Spring Security權(quán)限管理的投票器與表決機(jī)制怎么實(shí)現(xiàn)”吧!

1.投票器

先來(lái)看投票器。

在 Spring Security 中,投票器是由 AccessDecisionVoter 接口來(lái)規(guī)范的,我們來(lái)看下 AccessDecisionVoter 接口的實(shí)現(xiàn):

Spring Security權(quán)限管理的投票器與表決機(jī)制怎么實(shí)現(xiàn)

可以看到,投票器的實(shí)現(xiàn)有好多種,我們可以選擇其中一種或多種投票器,也可以自定義投票器,默認(rèn)的投票器是 WebExpressionVoter。

我們來(lái)看 AccessDecisionVoter 的定義:

public interface AccessDecisionVoter<s> {
	int ACCESS_GRANTED = 1;
	int ACCESS_ABSTAIN = 0;
	int ACCESS_DENIED = -1;
	boolean supports(ConfigAttribute attribute);
	boolean supports(Class<!--?--> clazz);
	int vote(Authentication authentication, S object,
			Collection<configattribute> attributes);
}

我稍微解釋下:

  1. 首先一上來(lái)定義了三個(gè)常量,從常量名字中就可以看出每個(gè)常量的含義,1 表示贊成;0 表示棄權(quán);-1 表示拒絕。

  2. 兩個(gè) supports 方法用來(lái)判斷投票器是否支持當(dāng)前請(qǐng)求。

  3. vote 則是具體的投票方法。在不同的實(shí)現(xiàn)類中實(shí)現(xiàn)。三個(gè)參數(shù),authentication 表示當(dāng)前登錄主體;object 是一個(gè) ilterInvocation,里邊封裝了當(dāng)前請(qǐng)求;attributes 表示當(dāng)前所訪問(wèn)的接口所需要的角色集合。

我們來(lái)分別看下幾個(gè)投票器的實(shí)現(xiàn)。

1.1 RoleVoter

RoleVoter 主要用來(lái)判斷當(dāng)前請(qǐng)求是否具備該接口所需要的角色,我們來(lái)看下其 vote 方法:

public int vote(Authentication authentication, Object object,
		Collection<configattribute> attributes) {
	if (authentication == null) {
		return ACCESS_DENIED;
	}
	int result = ACCESS_ABSTAIN;
	Collection<!--? extends GrantedAuthority--> authorities = extractAuthorities(authentication);
	for (ConfigAttribute attribute : attributes) {
		if (this.supports(attribute)) {
			result = ACCESS_DENIED;
			for (GrantedAuthority authority : authorities) {
				if (attribute.getAttribute().equals(authority.getAuthority())) {
					return ACCESS_GRANTED;
				}
			}
		}
	}
	return result;
}

這個(gè)方法的判斷邏輯很簡(jiǎn)單,如果當(dāng)前登錄主體為 null,則直接返回 ACCESS_DENIED 表示拒絕訪問(wèn);否則就從當(dāng)前登錄主體 authentication 中抽取出角色信息,然后和 attributes 進(jìn)行對(duì)比,如果具備 attributes 中所需角色的任意一種,則返回 ACCESS_GRANTED 表示允許訪問(wèn)。例如 attributes 中的角色為 [a,b,c],當(dāng)前用戶具備 a,則允許訪問(wèn),不需要三種角色同時(shí)具備。

另外還有一個(gè)需要注意的地方,就是 RoleVoter 的 supports 方法,我們來(lái)看下:

public class RoleVoter implements AccessDecisionVoter<object> {
	private String rolePrefix = "ROLE_";
	public String getRolePrefix() {
		return rolePrefix;
	}
	public void setRolePrefix(String rolePrefix) {
		this.rolePrefix = rolePrefix;
	}
	public boolean supports(ConfigAttribute attribute) {
		if ((attribute.getAttribute() != null)
				&amp;&amp; attribute.getAttribute().startsWith(getRolePrefix())) {
			return true;
		}
		else {
			return false;
		}
	}
	public boolean supports(Class<!--?--> clazz) {
		return true;
	}
}

可以看到,這里涉及到了一個(gè) rolePrefix 前綴,這個(gè)前綴是 ROLE_,在 supports 方法中,只有主體角色前綴是 ROLE_,這個(gè) supoorts 方法才會(huì)返回 true,這個(gè)投票器才會(huì)生效。

1.2 RoleHierarchyVoter

RoleHierarchyVoter 是 RoleVoter 的一個(gè)子類,在 RoleVoter 角色判斷的基礎(chǔ)上,引入了角色分層管理,也就是角色繼承,關(guān)于角色繼承,小伙伴們可以參考松哥之前的文章(Spring Security 中如何讓上級(jí)擁有下級(jí)的所有權(quán)限?)。

RoleHierarchyVoter 類的 vote 方法和 RoleVoter 一致,唯一的區(qū)別在于 RoleHierarchyVoter 類重寫了 extractAuthorities 方法。

@Override
Collection<!--? extends GrantedAuthority--> extractAuthorities(
		Authentication authentication) {
	return roleHierarchy.getReachableGrantedAuthorities(authentication
			.getAuthorities());
}

角色分層之后,需要通過(guò) getReachableGrantedAuthorities 方法獲取實(shí)際具備的角色

1.3 WebExpressionVoter

這是一個(gè)基于表達(dá)式權(quán)限控制的投票器,松哥后面專門花點(diǎn)時(shí)間和小伙伴們聊一聊基于表達(dá)式的權(quán)限控制,這里我們先不做過(guò)多展開,簡(jiǎn)單看下它的 vote 方法:

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;
}

這里代碼實(shí)際上就是根據(jù)傳入的 attributes 屬性構(gòu)建 weca 對(duì)象,然后根據(jù)傳入的 authentication 參數(shù)構(gòu)建 ctx 對(duì)象,最后調(diào)用 evaluateAsBoolean 方法去判斷權(quán)限是否匹配。

上面介紹這三個(gè)投票器是我們?cè)趯?shí)際開發(fā)中使用較多的三個(gè)。

1.4 其他

另外還有幾個(gè)比較冷門的投票器,松哥也稍微說(shuō)下,小伙伴們了解下。

Jsr250Voter

處理 Jsr-250 權(quán)限注解的投票器,如 @PermitAll,@DenyAll 等。

AuthenticatedVoter

AuthenticatedVoter 用于判斷 ConfigAttribute 上是否擁有 IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED、IS_AUTHENTICATED_ANONYMOUSLY 三種角色。

IS_AUTHENTICATED_FULLY 表示當(dāng)前認(rèn)證用戶必須是通過(guò)用戶名/密碼的方式認(rèn)證的,通過(guò) RememberMe 的方式認(rèn)證無(wú)效。

IS_AUTHENTICATED_REMEMBERED 表示當(dāng)前登錄用戶必須是通過(guò) RememberMe 的方式完成認(rèn)證的。

IS_AUTHENTICATED_ANONYMOUSLY 表示當(dāng)前登錄用戶必須是匿名用戶。

當(dāng)項(xiàng)目引入 RememberMe 并且想?yún)^(qū)分不同的認(rèn)證方式時(shí),可以考慮這個(gè)投票器。

AbstractAclVoter

提供編寫域?qū)ο?ACL 選項(xiàng)的幫助方法,沒(méi)有綁定到任何特定的 ACL 系統(tǒng)。

PreInvocationAuthorizationAdviceVoter

使用 @PreFilter 和 @PreAuthorize 注解處理的權(quán)限,通過(guò) PreInvocationAuthorizationAdvice 來(lái)授權(quán)。

當(dāng)然,如果這些投票器不能滿足需求,也可以自定義。

2.表決機(jī)制

一個(gè)請(qǐng)求不一定只有一個(gè)投票器,也可能有多個(gè)投票器,所以在投票器的基礎(chǔ)上我們還需要表決機(jī)制。

Spring Security權(quán)限管理的投票器與表決機(jī)制怎么實(shí)現(xiàn)

表決相關(guān)的類主要是三個(gè):

  • AffirmativeBased

  • ConsensusBased

  • UnanimousBased

他們的繼承關(guān)系如上圖。

三個(gè)決策器都會(huì)把項(xiàng)目中的所有投票器調(diào)用一遍,默認(rèn)使用的決策器是 AffirmativeBased。

三個(gè)決策器的區(qū)別如下:

  • AffirmativeBased:有一個(gè)投票器同意了,就通過(guò)。

  • ConsensusBased:多數(shù)投票器同意就通過(guò),平局的話,則看 allowIfEqualGrantedDeniedDecisions 參數(shù)的取值。

  • UnanimousBased 所有投票器都同意,請(qǐng)求才通過(guò)。

這里的具體判斷邏輯比較簡(jiǎn)單,松哥就不貼源碼了,感興趣的小伙伴可以自己看看。

3.在哪里配置

當(dāng)我們使用基于表達(dá)式的權(quán)限控制時(shí),像下面這樣:

http.authorizeRequests()
        .antMatchers("/admin/**").hasRole("admin")
        .antMatchers("/user/**").hasRole("user")
        .anyRequest().fullyAuthenticated()

那么默認(rèn)的投票器和決策器是在 AbstractInterceptUrlConfigurer#createDefaultAccessDecisionManager 方法中配置的:

private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
	AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));
	return postProcess(result);
}
List<accessdecisionvoter<?>&gt; getDecisionVoters(H http) {
	List<accessdecisionvoter<?>&gt; decisionVoters = new ArrayList&lt;&gt;();
	WebExpressionVoter expressionVoter = new WebExpressionVoter();
	expressionVoter.setExpressionHandler(getExpressionHandler(http));
	decisionVoters.add(expressionVoter);
	return decisionVoters;
}

這里就可以看到默認(rèn)的決策器和投票器,并且決策器 AffirmativeBased 對(duì)象創(chuàng)建好之后,還調(diào)用 postProcess 方法注冊(cè)到 Spring 容器中去了,結(jié)合松哥本系列前面的文章,大家知道,如果我們想要修改該對(duì)象就非常容易了:

http.authorizeRequests()
        .antMatchers("/admin/**").hasRole("admin")
        .antMatchers("/user/**").hasRole("user")
        .anyRequest().fullyAuthenticated()
        .withObjectPostProcessor(new ObjectPostProcessor<affirmativebased>() {
            @Override
            public <o extends affirmativebased> O postProcess(O object) {
                List<accessdecisionvoter<?>&gt; decisionVoters = new ArrayList&lt;&gt;();
                decisionVoters.add(new RoleHierarchyVoter(roleHierarchy()));
                AffirmativeBased affirmativeBased = new AffirmativeBased(decisionVoters);
                return (O) affirmativeBased;
            }
        })
        .and()
        .csrf()
        .disable();

這里只是給大家一個(gè)演示,正常來(lái)說(shuō)我們是不需要這樣修改的。當(dāng)我們使用不同的權(quán)限配置方式時(shí),會(huì)有自動(dòng)配置對(duì)應(yīng)的投票器和決策器?;蛘呶覀兪謩?dòng)配置投票器和決策器,如果是系統(tǒng)配置好的,大部分情況下并不需要我們修改。

感謝各位的閱讀,以上就是“Spring Security權(quán)限管理的投票器與表決機(jī)制怎么實(shí)現(xiàn)”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Spring Security權(quán)限管理的投票器與表決機(jī)制怎么實(shí)現(xiàn)這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI