亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

Spring實現處理跨域請求代碼詳解

2024-07-13 10:14:23
字體:
來源:轉載
供稿:網友

一次正常的請求

最近別人需要調用我們系統的某一個功能,對方希望提供一個api讓其能夠更新數據。由于該同學是客戶端開發,于是有了類似以下代碼。

@RequestMapping(method = RequestMethod.POST, value = "/update.json", produces = MediaType.APPLICATION_JSON_VALUE)public @ResponseBody Contacter update(@RequestBody Contacter contacterRO) {	logger.debug("get update request {}", contacterRO.toString());	if (contacterRO.getUserId() == 123) {		contacterRO.setUserName("adminUpdate-wangdachui");	}	return contacterRO;}

客戶端通過代碼發起http請求來調用。接著,該同學又提出:希望通過瀏覽器使用js調用,于是便有跨域問題。

為何跨域

簡單的說即為瀏覽器限制訪問A站點下的js代碼對B站點下的url進行ajax請求。假如當前域名是www.abc.com,那么在當前環境中運行的js代碼,出于安全考慮,正常情況下不能訪問www.zzz.com域名下的資源。

例如:以下代碼再本域名下可以通過js代碼正常調用接口

(function() {	var url = "http://localhost:8080/api/Home/update.json";	var data = {	    "userId": 123,	    "userName": "wangdachui"	  };	$.ajax({		url: url,		    type: 'POST',		    dataType: 'json',		    data: $.toJSON(data),		    contentType: 'application/json'	}	).done(function(result) {		console.log("success");		console.log(result);	}	).fail(function() {		console.log("error");	}	)})()

輸出為:

Object {userId: 123, userName: "adminUpdate-wangdachui"}

但是在其他域名下訪問則出錯:

OPTIONS http://localhost:8080/api/Home/update.jsonXMLHttpRequest cannot load http://localhost:8080/api/Home/update.json. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.

解決方案

JSONP

使用jsonp來進行跨域是一種比較常見的方式,但是在接口已經寫好的情況下,無論是服務端還是調用端都需要進行改造且要兼容原來的接口,工作量偏大,于是我們考慮其他方法。

CORS協議

按照參考資料的說法:每一個頁面需要返回一個名為‘Access-Control-Allow-Origin'的HTTP頭來允許外域的站點訪問。你可以僅僅暴露有限的資源和有限的外域站點訪問。在COR模式中,訪問控制的職責可以放到頁面開發者的手中,而不是服務器管理員。當然頁面開發者需要寫專門的處理代碼來允許被外域訪問。 我們可以理解為:如果一個請求需要允許跨域訪問,則需要在http頭中設置Access-Control-Allow-Origin來決定需要允許哪些站點來訪問。如假設需要允許www.foo.com這個站點的請求跨域,則可以設置:Access-Control-Allow-Origin:http://www.foo.com。或者Access-Control-Allow-Origin: * 。 CORS作為HTML5的一部分,在大部分現代瀏覽器中有所支持。

CORS具有以下常見的header

Access-Control-Allow-Origin: http://foo.orgAccess-Control-Max-Age: 3628800Access-Control-Allow-Methods: GET,PUT, DELETEAccess-Control-Allow-Headers: content-type"Access-Control-Allow-Origin"表明它允許"http://foo.org"發起跨域請求"Access-Control-Max-Age"表明在3628800秒內,不需要再發送預檢驗請求,可以緩存該結果"Access-Control-Allow-Methods"表明它允許GET、PUT、DELETE的外域請求"Access-Control-Allow-Headers"表明它允許跨域請求包含content-type頭

CORS基本流程

首先發出預檢驗(Preflight)請求,它先向資源服務器發出一個OPTIONS方法、包含“Origin”頭的請求。該回復可以控制COR請求的方法,HTTP頭以及驗證等信息。只有該請求獲得允許以后,才會發起真實的外域請求。

Spring MVC支持CORS

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.

從以上這段錯誤信息中我們可以看到,直接原因是因為請求頭中沒有Access-Control-Allow-Origin這個頭。于是我們直接想法便是在請求頭中加上這個header。服務器能夠返回403,表明服務器確實對請求進行了處理。

MVC 攔截器

首先我們配置一個攔截器來攔截請求,將請求的頭信息打日志。

DEBUG requestURL:/api/Home/update.json DEBUG method:OPTIONS DEBUG header host:localhost:8080 DEBUG header connection:keep-alive DEBUG header cache-control:max-age=0 DEBUG header access-control-request-method:POST DEBUG header origin:null DEBUG header user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36 DEBUG header access-control-request-headers:accept, content-type DEBUG header accept:*/* DEBUG header accept-encoding:gzip, deflate, sdch DEBUG header accept-language:zh-CN,zh;q=0.8,en;q=0.6 

在postHandle里打印日志發現,此時response的status為403。跟蹤SpringMVC代碼發現,在org.springframework.web.servlet.DispatcherServlet.doDispatch中會根據根據request來獲取HandlerExecutionChain,SpringMVC在獲取常規的處理器后會檢查是否為跨域請求,如果是則替換原有的實例。

@Overridepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {	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);	}	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);	if (CorsUtils.isCorsRequest(request)) {		CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);	}	return executionChain;}

檢查的方法也很簡單,即檢查請求頭中是否有origin字段

public static boolean isCorsRequest(HttpServletRequest request) {	return (request.getHeader(HttpHeaders.ORIGIN) != null);}

請求接著會交由 HttpRequestHandlerAdapter.handle來處理,根據handle不同,處理不同的邏輯。前面根據請求頭判斷是一個跨域請求,獲取到的Handler為PreFlightHandler,其實現為:

@Overridepublic void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {	corsProcessor.processRequest(this.config, request, response);}

繼續跟進

@Overridepublic Boolean processRequest(CorsConfiguration config, HttpServletRequest request, HttpServletResponse response)		throws IOException {	if (!CorsUtils.isCorsRequest(request)) {		return true;	}	ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);	ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);	if (WebUtils.isSameOrigin(serverRequest)) {		logger.debug("Skip CORS processing, request is a same-origin one");		return true;	}	if (responseHasCors(serverResponse)) {		logger.debug("Skip CORS processing, response already contains /"Access-Control-Allow-Origin/" header");		return true;	}	Boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);	if (config == null) {		if (preFlightRequest) {			rejectRequest(serverResponse);			return false;		} else {			return true;		}	}	return handleInternal(serverRequest, serverResponse, config, preFlightRequest);}

此方法首先會檢查是否為跨域請求,如果不是則直接返回,接著檢查是否同一個域下,或者response頭里是否具有Access-Control-Allow-Origin字段或者request里是否具有Access-Control-Request-Method。如果滿足判斷條件,則拒絕這個請求。

由此我們知道,可以通過在檢查之前設置response的Access-Control-Allow-Origin頭來通過檢查。我們在攔截器的preHandle的處理。加入如下代碼:

response.setHeader("Access-Control-Allow-Origin", "*");

此時瀏覽器中OPTIONS請求返回200。但是依然報錯:

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

我們注意到:在request的請求頭里有Access-Control-Request-Headers:accept, content-type,但是這個請求頭的中沒有,此時瀏覽器沒有據需發送請求。嘗試在response中加入:

response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

執行成功:Object {userId: 123, userName: “adminUpdate-wangdachui”}。

至此:我們通過分析原理使SpringMVC實現跨域,原有實現以及客戶端代碼不需要任何改動。

SpringMVC 4

此外,在參考資料2中,SpringMVC4提供了非常方便的實現跨域的方法。
在requestMapping中使用注解。 @CrossOrigin(origins = “http://localhost:9000”)
全局實現 .定義類繼承WebMvcConfigurerAdapter

public class CorsConfigurerAdapter extends WebMvcConfigurerAdapter{	@Override		public void addCorsMappings(CorsRegistry registry) {		registry.addMapping("/api/*").allowedOrigins("*");	}}

將該類注入到容器中:

<bean class="com.tmall.wireless.angel.web.config.CorsConfigurerAdapter"></bean>

總結

以上就是本文關于Spring實現處理跨域請求代碼詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題。如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97热在线精品视频在线观看| 欧美精品福利视频| 国产一区二区三区在线播放免费观看| 欧洲成人在线视频| 国产免费观看久久黄| 国语自产精品视频在线看抢先版图片| 性色av一区二区咪爱| 欧美极品xxxx| 九九热99久久久国产盗摄| 欧美另类极品videosbestfree| 国产精品美女久久久久久免费| 国产精品91在线观看| 亚洲精品日产aⅴ| 日韩免费在线观看视频| 久久97精品久久久久久久不卡| 国产69久久精品成人| 久久亚洲私人国产精品va| 久久激情视频免费观看| 欧美老女人xx| 成人亲热视频网站| 国模gogo一区二区大胆私拍| 欧美日韩国产91| 久久久在线视频| 国产精品青草久久久久福利99| 亚洲人成电影在线播放| 91黄色8090| 国产精品入口日韩视频大尺度| 亚洲精品v欧美精品v日韩精品| 欧美最猛黑人xxxx黑人猛叫黄| 欧美日韩黄色大片| 久久九九有精品国产23| 国自产精品手机在线观看视频| 国自在线精品视频| 久久久999精品免费| 亚洲精品xxx| 日韩av在线最新| 国产啪精品视频| 日韩精品福利在线| 亚洲免费一级电影| 亚洲男人天堂古典| 国产日韩欧美在线视频观看| 亚洲视频专区在线| 欧美激情网友自拍| 日韩在线观看免费网站| 亚洲欧美三级伦理| 欧美一性一乱一交一视频| 久久av.com| 日韩精品在线第一页| 日韩av片永久免费网站| 欧美中文在线观看| 欧美午夜视频在线观看| 久久6精品影院| 日韩经典中文字幕在线观看| 4438全国亚洲精品在线观看视频| 日韩有码在线播放| 97在线视频国产| 亚洲va久久久噜噜噜久久天堂| 国产女精品视频网站免费| 日本久久久久久久久久久| 亚洲在线一区二区| 亚洲色图25p| 日韩精品在线第一页| 97精品久久久中文字幕免费| 欧美国产日韩精品| 日韩欧美国产骚| 国产美女久久久| 欧美国产日韩在线| 2019国产精品自在线拍国产不卡| 久久精品国产精品亚洲| 欧美日韩国产成人在线观看| 欧美另类xxx| 日韩在线小视频| 欧美成人久久久| 精品国产自在精品国产浪潮| 亚洲精品电影网站| 日韩av影视综合网| 中文字幕欧美在线| 66m—66摸成人免费视频| 日本欧美一二三区| 亚洲欧美日韩国产中文专区| 18久久久久久| 中文字幕精品—区二区| 久久免费视频观看| 日韩激情av在线免费观看| 欧美激情二区三区| 久久精品免费电影| 欧美电影电视剧在线观看| 国模精品一区二区三区色天香| 91精品久久久久久久| 亚洲精品在线不卡| 欧美日韩加勒比精品一区| 欧美在线www| 久久久久久久久综合| 日韩av理论片| 欧美性猛交xxxxx免费看| 久久精品99久久香蕉国产色戒| 97免费视频在线播放| 欧美黑人狂野猛交老妇| 久热精品视频在线观看一区| 国产精品视频一区二区三区四| 国产美女精彩久久| 欧美成aaa人片免费看| 两个人的视频www国产精品| 日韩大陆毛片av| 国产免费一区二区三区在线能观看| 亚洲丝袜一区在线| 中文字幕久热精品视频在线| 中文字幕av一区二区| 久久久女人电视剧免费播放下载| 激情懂色av一区av二区av| 亚洲黄色av网站| 亚洲美女自拍视频| 成人免费视频网址| 亚洲色图50p| 欧美成人免费全部| 午夜精品一区二区三区在线视| 亚洲天堂网在线观看| 国产97在线|日韩| 亚洲影视九九影院在线观看| 91精品在线影院| 亚洲乱码国产乱码精品精天堂| 黑人精品xxx一区| 国产精品视频1区| 国模精品系列视频| 久久久中精品2020中文| 色综合老司机第九色激情| 欧美日韩在线看| 亚洲天堂色网站| 欧美中文字幕视频在线观看| 国产色综合天天综合网| 国产精品久久久久影院日本| 性色av一区二区三区红粉影视| 色婷婷综合久久久久中文字幕1| 久久人体大胆视频| 国产剧情久久久久久| 亚洲激情成人网| 日韩人体视频一二区| 亚洲女人天堂成人av在线| 国产精品免费电影| 国产精品白丝av嫩草影院| 欧美日韩中文字幕在线视频| 成人网在线视频| 亚洲欧美日韩爽爽影院| 亚洲最新在线视频| 欧美—级a级欧美特级ar全黄| 亚洲成人免费在线视频| 欧美一级黄色网| 日本伊人精品一区二区三区介绍| 韩曰欧美视频免费观看| 国产日韩在线亚洲字幕中文| 在线看日韩欧美| 亚洲欧美色图片| 久久综合国产精品台湾中文娱乐网| 亚洲a级在线播放观看| 日韩亚洲欧美中文高清在线| 97久久久免费福利网址| 国产精品久久久久9999| 国产69精品久久久久99| 国产欧美va欧美va香蕉在线| 亚洲精品久久久久久久久久久久| 亚洲国产精品专区久久| 日本视频久久久| 欧美视频在线观看 亚洲欧| 久久夜色精品国产欧美乱|