国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

cors跨域之簡單請求與預檢請求(發送請求頭帶令牌token)

zsy888 / 2330人閱讀

摘要:所以跨域請求分兩種簡單請求和預檢請求。但對于第二個錯誤,好像沒法向第一種那樣,將預檢請求轉變為簡單請求,所以,只有尋找方法怎么在后端實現相應的預檢請求,來返回一個狀態碼,告訴瀏覽器此次跨域請求可以繼續。

引子

自從從JAVA偽全棧轉前端以來,學習的路上就充滿了荊棘(奇葩問題),而涉及前后端分離這個問題,對cors的應用不斷增多,暴露出的問題也接踵而至。
這兩天動手實踐基于Token的WEB后臺認證機制,看過諸多理論(較好一篇推薦),正所謂慮一千次,不如去做一次。 猶豫一萬次,不如實踐一次,所以就有了下文,關于token的生成,另外一篇文章會細講,本篇主要討論在發送ajax請求,頭部帶上自定義token驗證驗證,暴露出的跨域問題。

先說說定義

CORS:跨來源資源共享(CORS)是一份瀏覽器技術的規范,提供了 Web 服務從不同網域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,是 JSONP 模式的現代版。與 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以讓網頁設計師用一般的 XMLHttpRequest,這種方式的錯誤處理比JSONP要來的好,JSONP對于 RESTful 的 API 來說,發送 POST/PUT/DELET 請求將成為問題,不利于接口的統一。但另一方面,JSONP 可以在不支持 CORS 的老舊瀏覽器上運作。不過現代的瀏覽器(IE10以上)基本都支持 CORS。
預檢請求(option):在 CORS 中,可以使用 OPTIONS 方法發起一個預檢請求(一般都是瀏覽檢測到請求跨域時,會自動發起),以檢測實際請求是否可以被服務器所接受。預檢請求報文中的 Access-Control-Request-Method 首部字段告知服務器實際請求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服務器實際請求所攜帶的自定義首部字段。服務器基于從預檢請求獲得的信息來判斷,是否接受接下來的實際請求。

OPTIONS /resources/post-here/ HTTP/1.1 
Host: bar.other 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: en-us,en;q=0.5 
Accept-Encoding: gzip,deflate 
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 
Connection: keep-alive 
Origin: http://foo.example 
Access-Control-Request-Method: POST 
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

服務器所返回的 Access-Control-Allow-Methods 首部字段將所有允許的請求方法告知客戶端。該首部字段與 Allow 類似,但只能用于涉及到 CORS 的場景中。

問題描述

話不多說,先上代碼:

前端(ajax庫:vue-resource)
        userLogin:function(){
            this.$http({
                method:"post",
                url:"http://localhost:8089/StockAnalyse/LoginServlet",
                params:{"flag":"ajaxlogin","loginName":this.userInfo.id,"loginPwd":this.userInfo.psd}, 
                headers: {"Content-Type": "application/x-www-form-urlencoded"}, 
                credientials:false, 
                emulateJSON: true                    
            }).then(function(response){
                sessionStorage.setItem("token",response.data);
                this.isActive =false;
                document.querySelector("#showInfo").classList.toggle("isLogin");
            })                 
        }
后端相關配置:
        response.setHeader("Access-Control-Allow-Origin", "http://localhost"); //允許來之域名為http://localhost的請求        
    response.setHeader("Access-Control-Allow-Headers", "Origin,No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, userId, token");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); //請求允許的方法
    response.setHeader("Access-Control-Max-Age", "3600");    //身份認證(預檢)后,xxS以內發送請求不在需要預檢,既可以直接跳過預檢,進行請求(前面只是照貓畫虎,后面才理解)

關于上面一段代碼,是我的用戶首次登錄認證,生成token令牌,保存在sessionStorage中,供后面調用;需要說明的是,前端服務器地址是:localhost:80,后端服務器地址:localhost:8089,所以前后端涉及到跨域,自己在后端做了相應的跨域設置:response.setHeader("Access-Control-Allow-Origin", "http://localhost"); 所以登錄認證,安全的實現了跨域信息認證,后端相應發送回來了相應的token信息。
但獲取到token后,想在需要的時候,在請求的頭部攜帶上這個令牌,來做相應的身份認證,所以自己在請求中做了這些改動(有標注),后端沒改動,源碼:

        checkIdentity:function(){
            let token =sessionStorage.getItem("token");
            this.$http({
                method:"post",
                url:"http://localhost:8089/StockAnalyse/LoginServlet",
                params:{"flag":"checklogin","isLogin":true,"token":token}, 
                headers: {"Content-Type": "application/x-www-form-urlencoded"}, 
                headers:{"token":token},        //header中攜帶令牌信息            
                credientials:false, 
                emulateJSON: true                    
            }).then(function(response){
                console.log(response.data);
            })                 
        }   

但實際上在devtools打印了如下錯誤信息:Response to preflight request doesn"t pass access control check: No "Access-Control-Allow-Origin" header is present on the requested resource. Origin "http://localhost" is therefore not allowed access.仔細想一想,好像,似乎這個問題遇到過,還提過問,確實提過,鏈接在這里。但這次的設置和上次一樣,就在header里多加了一個自定義token,但卻報了和上次沒有設置headers: {"Content-Type": "application/x-www-form-urlencoded"}一樣的錯誤信息,于是,不知所措,算了,重頭再來,好好百度,研究一下cors跨域。

理論學習

運氣不錯,找到了一篇好文,文章講的很細,也找到自己問題的所在:觸發 CORS 預檢請求。引用原文的話加以自己總結:跨域資源共享標準新增了一組 HTTP 首部字段,允許服務器聲明哪些源站有權限訪問哪些資源。另外,規范要求,對那些可能對服務器數據產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request:似曾相識有沒有?誒,對,上面那個錯誤信息中,就有一個這樣陌生的詞匯),從而獲知服務端是否允許該跨域請求。服務器確認允許之后,才發起實際的 HTTP 請求。在預檢請求的返回中,服務器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)。所以跨域請求分兩種:簡單請求和預檢請求。一次完整的請求不需要服務端預檢,直接響應的,歸為簡單請求;而響應前需要預檢的,稱為預檢請求,只有預檢請求通過,才有接下來的簡單請求。對于那些是簡單請求,那些會觸發預檢請求,文章做了詳細的總結,這里列出觸發預檢請求的條件(不知道腦子為啥會想到那些會觸發BFC的條件),不要跑題,原文是這樣總結的:

當請求滿足下述任一條件時,即應首先發送預檢請求:
使用了下面任一 HTTP 方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
人為設置了對 CORS 安全的首部字段集合之外的其他首部字段。該集合為:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
 Content-Type 的值不屬于下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
問題分析

所以,再來看自己兩次犯錯(第一次是沒有設置:headers: {"Content-Type": "application/x-www-form-urlencoded"}, 第二次是設置自定義header,headers:{"token":token}。很巧,有沒有,一次少,一次多,都點燃了導火索),其實都是觸發了預檢請求。對于第一次的錯誤,很好解決,增加headers: {"Content-Type": "application/x-www-form-urlencoded"},就解決了,關于Conten-Type的幾種取值,你需要知道的。但對于第二個錯誤,好像沒法向第一種那樣,將預檢請求轉變為簡單請求,所以,只有尋找方法怎么在后端實現相應的預檢請求,來返回一個狀態碼2xx,告訴瀏覽器此次跨域請求可以繼續。所以注意力轉向后端。
關于JAVA實現預檢請求,基本都是采用過濾器,不要問我為什么不是監聽器或者攔截器(我就是個偽全棧,就不要相互為難了,自己百度之),自定義(copy)了一個filter,并在web.xml中進行了設置。源碼:

Filter接口實現部分:
package stock.model;
import java.io.IOException;   
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;    
import org.apache.commons.httpclient.HttpStatus;   //這里需要添加commons-httpclient-3.1.jar
public class CorsFilter implements Filter {     //filter 接口的自定義實現
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        response.setHeader("Access-Control-Allow-Origin", "*");           
        String token = request.getHeader("token");
        System.out.println("filter origin:"+token);//通過打印,可以看到一次非簡單請求,會被過濾兩次,即請求兩次,第一次請求確認是否符合跨域要求(預檢),這一次是不帶headers的自定義信息,第二次請求會攜帶自定義信息。
        if ("OPTIONS".equals(request.getMethod())){//這里通過判斷請求的方法,判斷此次是否是預檢請求,如果是,立即返回一個204狀態嗎,標示,允許跨域;預檢后,正式請求,這個方法參數就是我們設置的post了
          response.setStatus(HttpStatus.SC_NO_CONTENT); //HttpStatus.SC_NO_CONTENT = 204
          response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS, DELETE");//當判定為預檢請求后,設定允許請求的方法
          response.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token"); //當判定為預檢請求后,設定允許請求的頭部類型
          response.addHeader("Access-Control-Max-Age", "1");  // 預檢有效保持時間                       
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {     
    }
}
web.xml配置部分

cors
stock.model.CorsFilter


cors
/*
   
關于Access-Control-Max-Age

最近又開始寫java,再回來看這個,發現當時Access-Control-Max-Age設置了1.其實這樣寫有很大問題,因為每個復雜請求都會發兩次。顯然這樣是當代所不能接受的,所以Max-Age的值適合設的大一些,具體多大很業務需求相關。另外Access-Control-Max-Age不是針對請求域名有效的,是請求的完成路徑有效的,比如第一次發出www.exanple.com/api/corsGet,會產生一次options請求和一次post請求,然后我再請求一次,這時沒有預檢請求了,只有post請求。但再發送一次www.exanple.com/api/corsSave請求,會發現又產生了一次options請求和一次post請求,所以Access-Control-Max-Age不是針對相同的origin有效,而是針對相同的requestUrl有效。很重要哦。

結論

當在后端實現添加上面的源碼后,皆大歡喜,問題得以解決,補上失敗和成功,自己截下的兩張請求響應圖。仔細看請求響應失敗發起響應那張圖,在General的數據集中,可以看到方法是options,而非代碼指定的post請求,所以這是一次瀏覽器發出的一次預檢請求,讓服務器確認此IP是否有訪問的權限,如果有,服務器需要返回一個2xx的狀態碼給瀏覽器。緊接著再發起一次簡單請求。如下面在devtools中的截取圖片(為了對比清除,我把兩次分別截取,做了拼接,因為不會做動態圖)。可以看到同一個post請求,實際上產生了兩次網絡連接。
但關于cors,要去探索的,還有很多很多,所以遵循革命語錄:實踐(有時也可以是時間)是檢驗真理的唯一標準,是沒有錯的。后續有新的收獲,再補充。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83724.html

相關文章

  • cors域之簡單請求預檢請求發送請求頭帶令牌token

    摘要:所以跨域請求分兩種簡單請求和預檢請求。但對于第二個錯誤,好像沒法向第一種那樣,將預檢請求轉變為簡單請求,所以,只有尋找方法怎么在后端實現相應的預檢請求,來返回一個狀態碼,告訴瀏覽器此次跨域請求可以繼續。 引子 自從從JAVA偽全棧轉前端以來,學習的路上就充滿了荊棘(奇葩問題),而涉及前后端分離這個問題,對cors的應用不斷增多,暴露出的問題也接踵而至。這兩天動手實踐基于Token的WE...

    RaoMeng 評論0 收藏0
  • 前端域之原因&&方案&&原理

    摘要:于是乎同源策略應運而生主要限制在于和無法讀取。怎么繞過同源策略首先一般來說協議和端口造成的跨域問題大部分方法是沒有辦法繞過的。二級域名是寄存在主域名之下的域名。當主域名受到懲罰二級域名也會連帶懲罰。 前言 這是一道前端跨不過躲不掉面試必備的知識,掙扎多年沒能做到刻骨銘心深入脊髓,只能好好寫篇博文記錄起來了; 什么是跨域? 廣義來說,A域執行的文檔腳本試圖去請求B域下的資源是不被允許的,...

    Zack 評論0 收藏0
  • CORS原理及@koa/cors源碼解析

    摘要:服務端根據這個值,決定是否同意本次請求。預檢請求預檢請求用的請求方法是,表示這個請求是用來詢問的。該字段也可以設為星號,表示同意任意跨源請求。這是為了避免多次預檢請求。 首發于個人博客 目錄 跨域 簡單請求和復雜請求 服務端如何設置CORS @koa/cors是怎么實現的 跨域 為什么會有跨域問題? 這是瀏覽器的同源策略所造成的,同源策略限制了從同一個源加載的文檔或腳本如何與來自...

    loostudy 評論0 收藏0
  • 你不知道的CORS跨域資源共享

    摘要:同源策略禁止使用對象向不同源的服務器地址發起請求。借助于決解同源策略決解同源策略,新方案跨域資源共享這里講的重點跨域資源共享提供的標準跨域解決方案,是一個由瀏覽器共同遵循的一套控制策略,通過的來進行交互主要通過后端來設置配置項。 了解下同源策略 源(origin)*:就是協議、域名和端口號; 同源: 就是源相同,即協議、域名和端口完全相同; 同源策略:同源策略是瀏覽器的一個安全...

    Gu_Yan 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<