国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Spring實(shí)現(xiàn)處理跨域請求代碼詳解

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

2021-03-02 10:39滄海一滴 Java教程

這篇文章主要介紹了Spring實(shí)現(xiàn)處理跨域請求代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以了解下。

一次正常的請求

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

?
1
2
3
4
5
6
7
8
@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請求來調(diào)用。接著,該同學(xué)又提出:希望通過瀏覽器使用js調(diào)用,于是便有跨域問題。

為何跨域

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(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");
    }
    )
}
)()

輸出為:

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

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

?
1
2
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è)請求需要允許跨域訪問,則需要在http頭中設(shè)置Access-Control-Allow-Origin來決定需要允許哪些站點(diǎn)來訪問。如假設(shè)需要允許www.foo.com這個(gè)站點(diǎn)的請求跨域,則可以設(shè)置:Access-Control-Allow-Origin:http://www.foo.com?;蛘逜ccess-Control-Allow-Origin: * 。 CORS作為HTML5的一部分,在大部分現(xiàn)代瀏覽器中有所支持。

CORS具有以下常見的header

?
1
2
3
4
5
6
7
8
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ā)起跨域請求
"Access-Control-Max-Age"表明在3628800秒內(nèi),不需要再發(fā)送預(yù)檢驗(yàn)請求,可以緩存該結(jié)果
"Access-Control-Allow-Methods"表明它允許GET、PUT、DELETE的外域請求
"Access-Control-Allow-Headers"表明它允許跨域請求包含content-type頭

CORS基本流程

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

Spring MVC支持CORS

?
1
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.

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

MVC 攔截器

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

?
1
2
3
4
5
6
7
8
9
10
11
12
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中會根據(jù)根據(jù)request來獲取HandlerExecutionChain,SpringMVC在獲取常規(guī)的處理器后會檢查是否為跨域請求,如果是則替換原有的實(shí)例。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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;
}

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@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);
}

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

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

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

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

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

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

?
1
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)以及客戶端代碼不需要任何改動。

SpringMVC 4

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

?
1
2
3
4
5
6
public class CorsConfigurerAdapter extends WebMvcConfigurerAdapter{
    @Override
        public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/*").allowedOrigins("*");
    }
}

將該類注入到容器中:

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

總結(jié)

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

原文鏈接:https://www.cnblogs.com/softidea/p/6108066.html

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 处女一级片 | 天天操天天碰 | 视频专区一区二区 | 国产欧美日韩专区 | 欧美中文在线 | av天天看| www操com | 亚洲精彩视频 | 6080yy午夜一二三区久久 | 日韩精品一二三区 | 免费久久99精品国产婷婷六月 | 精品伦精品一区二区三区视频 | 国产精品一二三区视频 | 国产福利在线视频 | 99精品久久久久久久免费 | 国产一级片儿 | 国产狂做受xxxxx高潮 | 亚洲高清视频在线 | 亚洲精品一区二三区不卡 | 国产欧美精品一区二区三区 | 亚洲国产美女视频 | 日韩福利在线 | 久久久久国产精品 | 黄一区 | 激情综合在线观看 | 欧美成年黄网站色视频 | 国产一区二区三区四区在线观看 | 视频1区2区 | 四虎久久精品 | 毛片视频免费 | 黄色一级片免费观看 | 91视频进入| 国产精品久久久久久久久费观看 | 四虎永久在线观看 | 综合久久99| 日本一区二区不卡 | 日本久久久久久 | 亚洲欧美一区二区三区四区 | 成人免费一区二区三区视频软件 | 国产一区二区三区在线视频 | 久久久性色精品国产免费观看 |