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

Spring實(shí)現(xiàn)處理跨域請(qǐng)求代碼詳解

 更新時(shí)間:2017年12月08日 14:18:23   作者:滄海一滴  
這篇文章主要介紹了Spring實(shí)現(xiàn)處理跨域請(qǐng)求代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以了解下。

一次正常的請(qǐng)求

最近別人需要調(diào)用我們系統(tǒng)的某一個(gè)功能,對(duì)方希望提供一個(gè)api讓其能夠更新數(shù)據(jù)。由于該同學(xué)是客戶端開發(fā),于是有了類似以下代碼。

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

客戶端通過代碼發(fā)起http請(qǐng)求來調(diào)用。接著,該同學(xué)又提出:希望通過瀏覽器使用js調(diào)用,于是便有跨域問題。

為何跨域

簡(jiǎn)單的說即為瀏覽器限制訪問A站點(diǎn)下的js代碼對(duì)B站點(diǎn)下的url進(jìn)行ajax請(qǐng)求。假如當(dāng)前域名是www.abc.com,那么在當(dāng)前環(huán)境中運(yùn)行的js代碼,出于安全考慮,正常情況下不能訪問www.zzz.com域名下的資源。

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

(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"}

但是在其他域名下訪問則出錯(cuò):

OPTIONS http://localhost:8080/api/Home/update.json
XMLHttpRequest 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來進(jìn)行跨域是一種比較常見的方式,但是在接口已經(jīng)寫好的情況下,無論是服務(wù)端還是調(diào)用端都需要進(jìn)行改造且要兼容原來的接口,工作量偏大,于是我們考慮其他方法。

CORS協(xié)議

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

CORS具有以下常見的header

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
"Access-Control-Allow-Origin"表明它允許"http://foo.org"發(fā)起跨域請(qǐng)求
"Access-Control-Max-Age"表明在3628800秒內(nèi),不需要再發(fā)送預(yù)檢驗(yàn)請(qǐng)求,可以緩存該結(jié)果
"Access-Control-Allow-Methods"表明它允許GET、PUT、DELETE的外域請(qǐng)求
"Access-Control-Allow-Headers"表明它允許跨域請(qǐng)求包含content-type頭

CORS基本流程

首先發(fā)出預(yù)檢驗(yàn)(Preflight)請(qǐng)求,它先向資源服務(wù)器發(fā)出一個(gè)OPTIONS方法、包含“Origin”頭的請(qǐng)求。該回復(fù)可以控制COR請(qǐng)求的方法,HTTP頭以及驗(yàn)證等信息。只有該請(qǐng)求獲得允許以后,才會(huì)發(fā)起真實(shí)的外域請(qǐng)求。

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.

從以上這段錯(cuò)誤信息中我們可以看到,直接原因是因?yàn)檎?qǐng)求頭中沒有Access-Control-Allow-Origin這個(gè)頭。于是我們直接想法便是在請(qǐng)求頭中加上這個(gè)header。服務(wù)器能夠返回403,表明服務(wù)器確實(shí)對(duì)請(qǐng)求進(jìn)行了處理。

MVC 攔截器

首先我們配置一個(gè)攔截器來攔截請(qǐng)求,將請(qǐng)求的頭信息打日志。

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里打印日志發(fā)現(xiàn),此時(shí)response的status為403。跟蹤SpringMVC代碼發(fā)現(xiàn),在org.springframework.web.servlet.DispatcherServlet.doDispatch中會(huì)根據(jù)根據(jù)request來獲取HandlerExecutionChain,SpringMVC在獲取常規(guī)的處理器后會(huì)檢查是否為跨域請(qǐng)求,如果是則替換原有的實(shí)例。

@Override
public 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;
}

檢查的方法也很簡(jiǎn)單,即檢查請(qǐng)求頭中是否有origin字段

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

請(qǐng)求接著會(huì)交由 HttpRequestHandlerAdapter.handle來處理,根據(jù)handle不同,處理不同的邏輯。前面根據(jù)請(qǐng)求頭判斷是一個(gè)跨域請(qǐng)求,獲取到的Handler為PreFlightHandler,其實(shí)現(xiàn)為:

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

繼續(xù)跟進(jìn)

@Override
public 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);
}

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

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

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

此時(shí)瀏覽器中OPTIONS請(qǐng)求返回200。但是依然報(bào)錯(cuò):

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

我們注意到:在request的請(qǐng)求頭里有Access-Control-Request-Headers:accept, content-type,但是這個(gè)請(qǐng)求頭的中沒有,此時(shí)瀏覽器沒有據(jù)需發(fā)送請(qǐng)求。嘗試在response中加入:

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

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

至此:我們通過分析原理使SpringMVC實(shí)現(xiàn)跨域,原有實(shí)現(xiàn)以及客戶端代碼不需要任何改動(dòng)。

SpringMVC 4

此外,在參考資料2中,SpringMVC4提供了非常方便的實(shí)現(xiàn)跨域的方法。
在requestMapping中使用注解。 @CrossOrigin(origins = “http://localhost:9000”)
全局實(shí)現(xiàn) .定義類繼承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>

總結(jié)

以上就是本文關(guān)于Spring實(shí)現(xiàn)處理跨域請(qǐng)求代碼詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題。如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!

相關(guān)文章

  • springboot集成mybatis-maven插件自動(dòng)生成pojo的詳細(xì)教程

    springboot集成mybatis-maven插件自動(dòng)生成pojo的詳細(xì)教程

    這篇文章主要介紹了springboot集成mybatis-maven插件自動(dòng)生成pojo的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • 詳解SpringBoot如何刪除引用jar包中的無用bean

    詳解SpringBoot如何刪除引用jar包中的無用bean

    為了趕速度和直接將之前多模塊的maven項(xiàng)目中的部分模塊,直接以jar包的形式引入到新項(xiàng)目中了,雖然省去了不少開發(fā)時(shí)間,導(dǎo)致項(xiàng)目臃腫,啟動(dòng)很慢。本文將用@ComponentScan注解去實(shí)現(xiàn)讓項(xiàng)目只加載自己需要的bean,需要的可以參考一下
    2022-06-06
  • SpringBoot超詳細(xì)講解集成Flink的部署與打包方法

    SpringBoot超詳細(xì)講解集成Flink的部署與打包方法

    昨天折騰了下SpringBoot與Flink集成,實(shí)際上集成特簡(jiǎn)單,主要是部署打包的問題折騰了不少時(shí)間。想打出的包直接可以java -jar運(yùn)行,同時(shí)也可以flink run運(yùn)行,或者在flink的dashboard上上傳點(diǎn)擊啟動(dòng)。結(jié)果是不行,但是使用不同的插件打包還是可以的
    2022-05-05
  • resty mail的簡(jiǎn)單發(fā)送郵件方法

    resty mail的簡(jiǎn)單發(fā)送郵件方法

    這篇文章主要為大家介紹了簡(jiǎn)單的resty mail發(fā)送郵件方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-03-03
  • CCF考試試題之門禁系統(tǒng)java解題代碼

    CCF考試試題之門禁系統(tǒng)java解題代碼

    這篇文章主要為大家詳細(xì)介紹了CCF考試試題之門禁系統(tǒng)java解題代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 基于Spring-cloud-gateway實(shí)現(xiàn)全局日志記錄的方法

    基于Spring-cloud-gateway實(shí)現(xiàn)全局日志記錄的方法

    最近項(xiàng)目在線上運(yùn)行出現(xiàn)了一些難以復(fù)現(xiàn)的bug需要定位相應(yīng)api的日志,通過nginx提供的api請(qǐng)求日志難以實(shí)現(xiàn),于是在gateway通過全局過濾器記錄api請(qǐng)求日志,本文給大家介紹基于Spring-cloud-gateway實(shí)現(xiàn)全局日志記錄,感興趣的朋友一起看看吧
    2023-11-11
  • IDEA2023 Maven3.9.1+Tomcat10.1.8配置并搭建Servlet5.0的框架實(shí)現(xiàn)

    IDEA2023 Maven3.9.1+Tomcat10.1.8配置并搭建Servlet5.0的框架實(shí)現(xiàn)

    本文主要介紹了IDEA2023 Maven3.9.1+Tomcat10.1.8配置并搭建Servlet5.0的框架實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • SpringMVC中的handlerMappings對(duì)象用法

    SpringMVC中的handlerMappings對(duì)象用法

    這篇文章主要介紹了SpringMVC中的handlerMappings對(duì)象用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • ActiveMQ安裝及部署教程圖解

    ActiveMQ安裝及部署教程圖解

    這篇文章主要介紹了ActiveMQ安裝及部署教程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Springboot跨域處理的多種方式小結(jié)

    Springboot跨域處理的多種方式小結(jié)

    當(dāng)一臺(tái)服務(wù)器資源從另一臺(tái)服務(wù)器(不同 的域名或者端口)請(qǐng)求一個(gè)資源或者接口,就會(huì)發(fā)起一個(gè)跨域 HTTP 請(qǐng)求,這篇文章主要介紹了Springboot跨域處理的多種方式小結(jié),需要的朋友可以參考下
    2023-11-11

最新評(píng)論