您好,登錄后才能下訂單哦!
這篇文章主要講解了“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)”吧!
先來(lái)看投票器。
在 Spring Security 中,投票器是由 AccessDecisionVoter 接口來(lái)規(guī)范的,我們來(lái)看下 AccessDecisionVoter 接口的實(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); }
我稍微解釋下:
首先一上來(lái)定義了三個(gè)常量,從常量名字中就可以看出每個(gè)常量的含義,1 表示贊成;0 表示棄權(quán);-1 表示拒絕。
兩個(gè) supports 方法用來(lái)判斷投票器是否支持當(dāng)前請(qǐng)求。
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)。
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) && 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ì)生效。
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í)際具備的角色
這是一個(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è)。
另外還有幾個(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)然,如果這些投票器不能滿足需求,也可以自定義。
一個(gè)請(qǐng)求不一定只有一個(gè)投票器,也可能有多個(gè)投票器,所以在投票器的基礎(chǔ)上我們還需要表決機(jī)制。
表決相關(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)單,松哥就不貼源碼了,感興趣的小伙伴可以自己看看。
當(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<?>> getDecisionVoters(H http) { List<accessdecisionvoter<?>> decisionVoters = new ArrayList<>(); 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<?>> decisionVoters = new ArrayList<>(); 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)注!
免責(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)容。