欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解讀SpringBoot中addCorsMappings配置跨域與攔截器互斥問題的原因

 更新時間:2023年12月26日 16:49:18   作者:huangyaa729  
這篇文章主要介紹了解讀SpringBoot中addCorsMappings配置跨域與攔截器互斥問題的原因,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

SpringBoot中addCorsMappings配置跨域與攔截器互斥

如題,前兩天在做前后端分離項目時,碰到了這個問題,登錄token驗證的攔截器使項目中配置的跨域配置失效,導(dǎo)致瀏覽器拋出跨域請求錯誤,跨域配置如下:

public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                        registry.addMapping("/**")
                        .allowedOrigins(origins)
                        .allowedHeaders("*")
                        .allowCredentials(true)
                        .allowedMethods("*")
                        .maxAge(3600);
            }
        };
    }

通過在網(wǎng)上的查詢,發(fā)現(xiàn)了如下解釋

  • 但是使用此方法配置之后再使用自定義攔截器時跨域相關(guān)配置就會失效。
  • 原因是請求經(jīng)過的先后順序問題,當(dāng)請求到來時會先進(jìn)入攔截器中,而不是進(jìn)入Mapping映射中,所以返回的頭信息中并沒有配置的跨域信息。瀏覽器就會報跨域異常。

然后參考了網(wǎng)上給出的方法,重新引入了跨域過濾器配置,解決了這個問題。

那最終這個問題產(chǎn)生的原因是什么的,真的如上訴所說嗎,我通過調(diào)試與研究源碼,找了原因。

在springMvc中,我們都知道路徑的映射匹配是通過DispatcherServlet這個類來實現(xiàn)的,最終的函數(shù)執(zhí)行在doDispatch()這個方法中:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request. 
		(1)mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
		(2)HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

		(3)if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
		(4)mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
		(5)mappedHandler.applyPostHandle(processedRequest, response, mv);
			}

在這個類中,我們關(guān)注(1)-(5)這幾句代碼,基本上整個映射執(zhí)行的邏輯就明了了:

  • (1)根據(jù)請求request獲取執(zhí)行器鏈(包括攔截器和最終執(zhí)行方法Handler)
  • (2)根據(jù)Handler獲取handlerAdapter;
  • (3)執(zhí)行執(zhí)行器鏈中的攔截方法(preHandle);
  • (4)執(zhí)行handler方法;
  • (5)執(zhí)行執(zhí)行器鏈中的攔截方法(postHandle);

在這個函數(shù)中我們并沒有看到什么時候執(zhí)行addCorsMappings這一配置內(nèi)容,那它到底是什么時候添加的呢,那就需要仔細(xì)分析步驟(1)了:獲取整個執(zhí)行器鏈。

通過調(diào)試定位,我發(fā)現(xiàn)getHandle()最終執(zhí)行的AbstractHandlerMapping這個類的函數(shù)

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
(1)Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}

(2)HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
(3)	executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

這個函數(shù)中我也標(biāo)記了(1)、(2)、(3)這三條語句:

  • (1)獲取request所需執(zhí)行的handler,具體邏輯不再細(xì)說,有興趣的可以參考我的另一篇文章
  • (2)獲取執(zhí)行器鏈,簡單來說就是把具體的執(zhí)行器和整個攔截器鏈組成一個鏈隊形,方便后續(xù)執(zhí)行;
  • (3)這個就是關(guān)鍵點,可能有的同學(xué)已經(jīng)看明白了,addCorsMapping配置就是在這塊引入的;

進(jìn)入這個方法后,一切都明了了;

protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
			HandlerExecutionChain chain, CorsConfiguration config) {

		if (CorsUtils.isPreFlightRequest(request)) {
			HandlerInterceptor[] interceptors = chain.getInterceptors();
			chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
		}
		else {
			chain.addInterceptor(new CorsInterceptor(config));
		}
		return chain;
	}

先判斷request是否是預(yù)檢請求(不明白什么是預(yù)檢請求的可以自身搜索相關(guān)解釋,很多,不再贅述),是預(yù)檢請求則生成個預(yù)檢執(zhí)行器PreFlightHandler,然后在doDispatch函數(shù)(4)中執(zhí)行;

否則生成一個跨域攔截器加入攔截器鏈中,最終再doDispatch函數(shù)(3)處執(zhí)行,而因為攔截器是順序執(zhí)行的,如果前面執(zhí)行失敗異常返回后,后面的則不再執(zhí)行。

所以當(dāng)跨越請求在攔截器那邊處理后就異常返回了,那么響應(yīng)的response報文頭部關(guān)于跨域允許的信息就沒有被正確設(shè)置,導(dǎo)致瀏覽器認(rèn)為服務(wù)不允許跨域,而造成錯誤;而當(dāng)我們使用過濾器時,過濾器先于攔截器執(zhí)行,那么無論是否被攔截,始終有允許跨域的頭部信息,就不會出問題了。

另注:

對于預(yù)檢請求,一般token驗證時是不會攔截此請求的,因為預(yù)檢請求不會附帶任何參數(shù)信息,也就沒有所需的token信息,所以攔截時需過濾預(yù)檢請求

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • MyBatis中的@SelectProvider注解源碼分析

    MyBatis中的@SelectProvider注解源碼分析

    這篇文章主要介紹了MyBatis中的@SelectProvider注解源碼分析,@SelectProvider功能就是用來單獨寫一個class類與方法,用來提供一些xml或者注解中不好寫的sql,今天就來說下這個注解的具體用法與源碼,需要的朋友可以參考下
    2024-01-01
  • Docker和?Containerd?的區(qū)別解析

    Docker和?Containerd?的區(qū)別解析

    containerd?是一個來自?Docker?的高級容器運(yùn)行時,并實現(xiàn)了?CRI?規(guī)范,它是從?Docker?項目中分離出來,之后?containerd?被捐贈給云原生計算基金會(CNCF)為容器社區(qū)提供創(chuàng)建新容器解決方案的基礎(chǔ),這篇文章主要介紹了Docker和?Containerd?的區(qū)別,需要的朋友可以參考下
    2024-03-03
  • Java構(gòu)造方法和方法重載詳解

    Java構(gòu)造方法和方法重載詳解

    大家好,本篇文章主要講的是Java構(gòu)造方法和方法重載詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • Java超詳細(xì)講解多線程中的Process與Thread

    Java超詳細(xì)講解多線程中的Process與Thread

    進(jìn)程process:在一定的環(huán)境下,把靜態(tài)的程序代碼運(yùn)行起來,通過使用不同的資源,來完成一定的任務(wù);線程thread:是程序中一個單一的順序控制流程。在單個進(jìn)程中同時運(yùn)行多個線程完成不同的工作,稱為多線程
    2022-05-05
  • Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐

    Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐

    本文主要介紹了Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • java中的動態(tài)代理與責(zé)任鏈模式詳解

    java中的動態(tài)代理與責(zé)任鏈模式詳解

    這篇文章主要介紹了java中的動態(tài)代理與責(zé)任鏈模式詳解,動態(tài)代理提供了一種靈活且非侵入式的方式,可以對對象的行為進(jìn)行定制和擴(kuò)展,它在代碼重用、解耦和業(yè)務(wù)邏輯分離、性能優(yōu)化以及系統(tǒng)架構(gòu)中起到了重要的作用,需要的朋友可以參考下
    2023-08-08
  • Java switch多值匹配操作詳解

    Java switch多值匹配操作詳解

    這篇文章主要介紹了Java switch多值匹配操作詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • 如何使用maven-helper插件解決jar包沖突問題

    如何使用maven-helper插件解決jar包沖突問題

    安裝了Maven?Helper插件,只要打開pom文件,就可以打開該pom文件的Dependency?Analyzer視圖,這篇文章主要介紹了使用maven-helper插件解決jar包沖突,需要的朋友可以參考下
    2024-05-05
  • Java通過JsApi方式實現(xiàn)微信支付

    Java通過JsApi方式實現(xiàn)微信支付

    本文講解了Java如何實現(xiàn)JsApi方式的微信支付,代碼內(nèi)容詳細(xì),文章思路清晰,需要的朋友可以參考下
    2015-07-07
  • java獲取登錄者IP和登錄時間的兩種實現(xiàn)代碼詳解

    java獲取登錄者IP和登錄時間的兩種實現(xiàn)代碼詳解

    這篇文章主要介紹了java獲取登錄者IP和登錄時間的實現(xiàn)代碼,本文通過兩種結(jié)合實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07

最新評論