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

Spring Security基于HttpRequest配置權(quán)限示例詳解

 更新時(shí)間:2024年03月29日 10:16:49   作者:Evan_L  
這篇文章主要介紹了Spring Security基于HttpRequest配置權(quán)限示例詳解,我們?cè)谂渲弥信渲玫膗rl被封裝成RequestMatcher,而hasRole被封裝成AuthorityAuthorizationManager,本文結(jié)合示例代碼講解的非常詳細(xì),需要的朋友可以參考下

前言

今天我們重點(diǎn)聊聊授權(quán)方式的另外一種:基于HttpServletRequest配置權(quán)限

基于HttpServletRequest配置權(quán)限

一個(gè)典型的配置demo

http.authorizeHttpRequests(requestMatcherRegstry -> 
// /admin/** 需要有AMIND角色
	requestMatcherRegstry.requestMatchers("/admin/**").hasRole("ADMIN")
			// /log/** 只要有AMIND、USER角色之一
          .requestMatchers("/log/**").hasAnyRole("ADMIN", "USER")
			// 任意請(qǐng)求 只要登錄了即可訪問(wèn)
          .anyRequest().authenticated()
);

從這里也可以看出,要實(shí)現(xiàn)基于RBAC,還是比較容易的。也比較容易使用。但是如果想要?jiǎng)討B(tài)的增加角色,就需要我們定制AuthorizationManager。

配置原理

HttpSecurity是負(fù)責(zé)構(gòu)建DefaultSecurityFilterChain的。而這個(gè)安全過(guò)濾器鏈,則是允許我們進(jìn)行配置的。而authorizeHttpRequests方法,正是配置AuthorizationFilter的。而我們傳入的入?yún)?lambada表達(dá)式-則是指引如何配置AuthorizationFilter的。

/**
 * 這個(gè)方法是HttpSecurity的方法。
 * 作用是配置AuthorizationFilter。
 * 其入?yún)uthorizeHttpRequestsCustomizer正是讓我們配置AuthorizationFilter的關(guān)鍵。
 * Customizer:就是定制。原理比較容易理解,就是我把你需要配置的東西丟給你,你往里面賦值。
 * AuthorizeHttpRequestsConfigurer<HttpSecurity>:這個(gè)是Configurer的實(shí)現(xiàn),負(fù)責(zé)引入過(guò)濾器的。這里明顯就是引入AuthorizationFilter
 * AuthorizationManagerRequestMatcherRegistry:這個(gè)就是我們最終配置的東西。而這個(gè)配置的正是我們上面的RequestMatcherDelegatingAuthorizationManager。說(shuō)白了就是往里面添加哪些路徑對(duì)應(yīng)哪些AuthorizationManager。只不過(guò),為了方便使用,也幫我們都封裝好了。不妨繼續(xù)往后看看。
 */
public HttpSecurity authorizeHttpRequests(
			Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
			throws Exception {
		ApplicationContext context = getContext();
		// 這里干了三個(gè)事情:
		// 1. 如果當(dāng)前HttpSecurity不存在AuthorizeHttpRequestsConfigurer,則創(chuàng)建一個(gè),并注冊(cè)到當(dāng)前的HttpSecurity對(duì)象中。
		// 2. 從AuthorizeHttpRequestsConfigurer拿到他的注冊(cè)器也就是AuthorizationManagerRequestMatcherRegistry
		// 3. 調(diào)用傳入的參數(shù)的customize。如此,我們傳入的lambda表達(dá)式就被調(diào)用了。
		authorizeHttpRequestsCustomizer
			.customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());
		return HttpSecurity.this;
	}
public final class AuthorizationManagerRequestMatcherRegistry
		extends AbstractRequestMatcherRegistry<AuthorizedUrl> {
	/**
	 * 這是父類(lèi)的方法
	 * C代表的是AuthorizedUrl
	 */
	public C requestMatchers(String... patterns) {
		// 調(diào)用的重載方法第一個(gè)參數(shù)為HttpMethod,也就是說(shuō),我們還可以指定HTTP請(qǐng)求的方法,例如:POST、GET等
		return requestMatchers(null, patterns);
	}
	@Override
	protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
		this.unmappedMatchers = requestMatchers;
		return new AuthorizedUrl(requestMatchers);
	}
}
public class AuthorizedUrl {
	private final List<? extends RequestMatcher> matchers;
	public AuthorizationManagerRequestMatcherRegistry permitAll() {
		return access(permitAllAuthorizationManager);
	}
	public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
		return access(withRoleHierarchy(AuthorityAuthorizationManager.hasRole(role)));
	}
	public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
		return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)));
	}
	public AuthorizationManagerRequestMatcherRegistry authenticated() {
		return access(AuthenticatedAuthorizationManager.authenticated());
	}
	public AuthorizationManagerRequestMatcherRegistry access(
		AuthorizationManager<RequestAuthorizationContext> manager) {
		Assert.notNull(manager, "manager cannot be null");
		return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
	}
}
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
	private AuthorizationManagerRequestMatcherRegistry addMapping(List<? extends RequestMatcher> matchers,
		AuthorizationManager<RequestAuthorizationContext> manager) {
		for (RequestMatcher matcher : matchers) {
			this.registry.addMapping(matcher, manager);
		}
		return this.registry;
	}
}

我們通過(guò)lambda表達(dá)式:

requestMatcherRegstry -> requestMatcherRegstry.requestMatchers("/admin/**").hasRole("ADMIN")

配置的正是AuthorizationManagerRequestMatcherRegistry
requestMachers方法,構(gòu)建出AuthorizedUrl,然后通過(guò)這個(gè)類(lèi)的hasRole方法注冊(cè)當(dāng)前路徑所對(duì)應(yīng)的權(quán)限/角色。這個(gè)對(duì)應(yīng)關(guān)系由RequestMatcherEntry保存。key:RequestMatcher requestMatcher;value: AuthorizationManager。

值得一提的是,這個(gè)lambda表達(dá)式以及其鏈?zhǔn)秸{(diào)用看起來(lái)簡(jiǎn)單方便,但是其內(nèi)部涉及多個(gè)類(lèi)的方法調(diào)用,實(shí)在很容易犯迷糊,這是我覺(jué)得比較詬病的地方。在我看來(lái),鏈?zhǔn)秸{(diào)用還是同一個(gè)返回值(每次都返回this)才能做到在方便至于也能清晰明了,容易理解。
而這里在lambda表達(dá)式內(nèi)部:

  • 第一個(gè)方法是requestMatcherRegstry.requestMatchers

AbstractRequestMatcherRegistry,也就是我們的AuthorizationManagerRequestMatcherRegistry的父類(lèi)。方法返回值是AuthorizedUrl。

  • 第二個(gè)方法是AuthorizedUrl.hasRole

而該方法的返回值為AuthorizationManagerRequestMatcherRegistry

發(fā)現(xiàn)什么了嗎?鏈?zhǔn)秸{(diào)用還能玩起遞歸,又回到最開(kāi)始的第一個(gè)方法了。而要是我們配置HttpSecurity,直接一連串的鏈?zhǔn)秸{(diào)用,那更是沒(méi)譜了。經(jīng)常就是,你只能看著別人這樣配置,然后照貓畫(huà)虎。這個(gè)鏈?zhǔn)秸{(diào)用咋調(diào)回來(lái)的,一頭霧。因?yàn)橹虚g可能跨越好幾個(gè)不同的類(lèi)。。。
PS:可能官方也有些意識(shí)到這點(diǎn),所以sample工程都是類(lèi)似于本文開(kāi)頭的那樣,傳入一個(gè)基于lambda表達(dá)式的Customizer。一個(gè)方法配置一個(gè)過(guò)濾器的SecurityConfigurer。但,如果你翻看源碼,你看到的就是一連串的鏈?zhǔn)秸{(diào)用。最為明顯的一個(gè)證明就是HttpSecurity#and方法過(guò)期了。因此個(gè)人推薦大家用文章開(kāi)頭的那種方法,相對(duì)清晰易理解。

我想說(shuō),這么玩是深怕別人搞明白了是嗎???更絕的是,即便你知曉了原理也沒(méi)有辦法直接注冊(cè)對(duì)應(yīng)關(guān)系,除非你使用反射!

這里給大家提個(gè)醒,如果你想搞明白你在使用SpringSecurity究竟在配置些什么,那么你就必須要搞明白上面的套路。

設(shè)計(jì)方案

Spring Security在5.5版本之后,在鑒權(quán)架構(gòu)上,進(jìn)行了較大的改動(dòng)。以至于官方也出了遷移指南

組件5.5之前5.5之后
過(guò)濾器FilterSecurityInterceptorAuthorizationFilter
鑒權(quán)管理器AccessDecisionManagerAuthorizationManager
訪問(wèn)決策投票員AccessDecisionVoter-

而原來(lái)的設(shè)計(jì)方案,相較于新的方案,更為復(fù)雜。這里給大家一張官方的UML感受感受:

除卻過(guò)濾器外,還需要三個(gè)組件來(lái)構(gòu)建完整的鑒權(quán):
AccessDecisionManager 、AccessDecisionVoter 、ConfigAttribute。

感興趣的同學(xué)可以自己琢磨琢磨,但已經(jīng)廢棄的方案,這里就不討論了。

5.6之后的新方案

新方案只有一個(gè)包羅萬(wàn)象、且極具擴(kuò)展性的AuthorizationManager

我們前面的配置demo,本質(zhì)上都是在配置RequestMatcherDelegatingAuthorizationManager。他主要是記錄每一個(gè)路徑對(duì)應(yīng)的AuthorizationManager<HttpServletRequest>。當(dāng)有請(qǐng)求過(guò)來(lái)時(shí),只需要遍歷每一個(gè)路徑,當(dāng)找到匹配者就委托該AuthorizationManager<HttpServletRequest>進(jìn)行鑒權(quán)。

在我們的配置demo中,對(duì)應(yīng)的是AuthoriztyAuthorizationManagerAuthenticatedAuthorizationManager。前者,意味著我們配置的是角色/權(quán)限,后者對(duì)應(yīng)的是authenticated()這個(gè)方法。

如果你認(rèn)真看了這個(gè)關(guān)系圖,那么一定會(huì)發(fā)現(xiàn)右邊的4個(gè)實(shí)現(xiàn)類(lèi)正是我們?cè)谏弦晃闹v述基于方法配置權(quán)限中所使用到的。

鑒權(quán)源碼分析

權(quán)限過(guò)濾的入口:AuthorizationFilter

public class AuthorizationFilter extends GenericFilterBean {
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
			throws ServletException, IOException {
		// 類(lèi)型轉(zhuǎn)換
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		// 是否需要執(zhí)行鑒權(quán)
		if (this.observeOncePerRequest && isApplied(request)) {
			chain.doFilter(request, response);
			return;
		}
		// /error和異步請(qǐng)求不處理
		if (skipDispatch(request)) {
			chain.doFilter(request, response);
			return;
		}
		// 是否已經(jīng)執(zhí)行過(guò)鑒權(quán)邏輯了
		String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
		try {
			// 從SecurityContextHolder中獲取憑證,并通過(guò)AuthorizationManager做出決策
			AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
			// 發(fā)布鑒權(quán)事件
			this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
			if (decision != null && !decision.isGranted()) {
				// 拒絕訪問(wèn)異常
				throw new AccessDeniedException("Access Denied");
			}
			// 正常執(zhí)行后續(xù)業(yè)務(wù)邏輯
			chain.doFilter(request, response);
		}
		finally {
			// 處理完業(yè)務(wù)邏輯后,為當(dāng)前請(qǐng)求清理標(biāo)識(shí)			
			request.removeAttribute(alreadyFilteredAttributeName);
		}
	}
}

RequestMatcherDelegatingAuthorizationManager

public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
@Override
	public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
		// 遍歷每一個(gè)已經(jīng)登錄好的路徑,找到對(duì)應(yīng)的AuthorizationManager<RequestAuthorizationContext>>
		for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {
			RequestMatcher matcher = mapping.getRequestMatcher();
			// 匹配當(dāng)前請(qǐng)求
			MatchResult matchResult = matcher.matcher(request);
			if (matchResult.isMatch()) {
				// 找到匹配的AuthorizationManager就直接調(diào)用check方法并返回鑒權(quán)結(jié)果
				AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
				return manager.check(authentication,
						new RequestAuthorizationContext(request, matchResult.getVariables()));
			}
		}
		// 沒(méi)有匹配的AuthorizationManager則返回拒絕當(dāng)前請(qǐng)求
		return DENY;
	}
}

可見(jiàn),在沒(méi)有匹配的AuthorizationManager的情況下,默認(rèn)是拒絕請(qǐng)求的。

總結(jié)

1.我們?cè)谂渲弥信渲玫膗rl被封裝成RequestMatcher,而hasRole被封裝成AuthorityAuthorizationManager。進(jìn)行注冊(cè),在請(qǐng)求過(guò)來(lái)時(shí),便通過(guò)遍歷所有注冊(cè)好的RequestMatch進(jìn)行匹配,存在匹配就調(diào)用AuthorizationManager<RequestAuthorizationContext>#check方法。

2.配置的鏈?zhǔn)秸{(diào)用,會(huì)跨越多個(gè)不同的類(lèi),最終又回到第一個(gè)對(duì)象的類(lèi)型。

后記

本文我們聊了基于HttpRequest配置權(quán)限的方方面面。相信這里有一個(gè)點(diǎn)應(yīng)該會(huì)引起大家的注意:配置。下一次,我們聊聊Spring Security的配置體系。

相關(guān)文章

  • 詳解JDBC使用

    詳解JDBC使用

    JDBC(Java Database Connectivity),即Java數(shù)據(jù)庫(kù)連接,是一種用于執(zhí)行SQL語(yǔ)句的Java API,可以為多種關(guān)系數(shù)據(jù)庫(kù)提供同一訪問(wèn),它由一組用Java語(yǔ)言編寫(xiě)的類(lèi)和接口組成。
    2017-05-05
  • Java泛型<T> T與T的使用方法詳解

    Java泛型<T> T與T的使用方法詳解

    這篇文章主要介紹了Java泛型<T> T與T的使用方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java方法重載實(shí)現(xiàn)原理及代碼實(shí)例

    Java方法重載實(shí)現(xiàn)原理及代碼實(shí)例

    這篇文章主要介紹了Java方法重載實(shí)現(xiàn)原理及代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java中如何自定義一個(gè)類(lèi)加載器加載自己指定的類(lèi)

    Java中如何自定義一個(gè)類(lèi)加載器加載自己指定的類(lèi)

    這篇文章主要給大家介紹了關(guān)于Java中如何自定義一個(gè)類(lèi)加載器加載自己指定的類(lèi),自定義類(lèi)加載器允許我們加載特定路徑的類(lèi)文件,并且可以用于插件系統(tǒng)、熱部署和隔離加載等場(chǎng)景,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-12-12
  • java面試常見(jiàn)模式問(wèn)題---單例模式

    java面試常見(jiàn)模式問(wèn)題---單例模式

    單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類(lèi)型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式
    2021-06-06
  • SpringBoot中@Autowired爆紅原理分析及解決

    SpringBoot中@Autowired爆紅原理分析及解決

    這篇文章主要介紹了SpringBoot中@Autowired爆紅原理分析及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • JVM角度調(diào)試優(yōu)化MyEclipse

    JVM角度調(diào)試優(yōu)化MyEclipse

    這篇文章主要介紹了從JVM角度對(duì)MyEclipse進(jìn)行調(diào)試優(yōu)化,為大家分析調(diào)試優(yōu)化MyEclipse的步驟,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Java匿名內(nèi)部類(lèi)的寫(xiě)法示例

    Java匿名內(nèi)部類(lèi)的寫(xiě)法示例

    這篇文章主要給大家介紹了關(guān)于Java匿名內(nèi)部類(lèi)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • SpringBoot部署SSL證書(shū)(JKS格式)

    SpringBoot部署SSL證書(shū)(JKS格式)

    文將介紹如何在Spring Boot應(yīng)用中部署SSL證書(shū),以實(shí)現(xiàn)安全傳輸和保護(hù)數(shù)據(jù)隱私,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • Mybatis動(dòng)態(tài)SQL?foreach批量操作方法

    Mybatis動(dòng)態(tài)SQL?foreach批量操作方法

    這篇文章主要介紹了Mybatis動(dòng)態(tài)SQL?foreach批量操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03

最新評(píng)論

延庆县| 青浦区| 井陉县| 寻甸| 工布江达县| 岫岩| 赫章县| 吐鲁番市| 封丘县| 武宁县| 会东县| 黎城县| 宁海县| 舞钢市| 中宁县| 曲麻莱县| 株洲市| 新化县| 都安| 鄄城县| 昂仁县| 恩平市| 叙永县| 遂平县| 文登市| 焦作市| 林州市| 溧水县| 吉木萨尔县| 城口县| 娄底市| 介休市| 大姚县| 色达县| 靖宇县| 白水县| 喀什市| 旬邑县| 铁岭县| 乐至县| 新巴尔虎左旗|