HTTP/1.1
一、簡介
HTTP協議:超文本傳輸協議(HyperText Transfer Protocol, HTTP),使用TCP作爲運輸層協議。HTTP協議的長連接和短連接,實質上是TCP協議的長連接和短連接。HTTP 是一個無狀態協議,但是web站點往往希望能夠識別用戶,因此有了cookie和session。
二、HTTP報文格式
Client request:
GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
Server response:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
Hello World! My payload includes a trailing CRLF.
1、HTTP請求報文
請求行:
請求行由請求方法字段、URL字段和HTTP協議版本字段3個字段組成,它們用空格分隔。例如,GET /index.html HTTP/1.1。
HTTP協議的請求方法有GET、POST、HEAD、PUT、DELETE、TRACE等。
注意:HTTP協議本身並沒有限制URL的長度,實際中URL長度限制是實現http協議的客戶端(瀏覽器等)和服務器限制的。
首部(Header Fields)
請求頭部由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號“:”分隔。請求頭部通知服務器有關於客戶端請求的信息,典型的首部頭(包括請求頭或者響應頭)有:
User-Agent:產生請求的瀏覽器類型。
Accept:客戶端可識別的內容類型列表。
Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機。
Cookie:該請求域名下的cookie值。
Set-Cookie:_security_token=51568727949026296;Path=/;Domain=.dasouche-fin.net;Max-Age=600000。
Content-Type:請求的與實體對應的MIME信息,application/x-www-form-urlencoded、text/html;charset=UTF-8等。
Connection:keep-alive,持久連接,HTTP/1.0需要顯示聲明,HTTP/1.1默認開啓。關閉:Connection:close。
Referer:該首部包含了當前請求頁面的來源頁面的地址,即表示當前頁面是通過此來源頁面裏的鏈接進入的。服務端一般使用 Referer
首部識別訪問來源,可能會以此進行統計分析、日誌記錄以及緩存優化等。
Accept-Language:瀏覽器可接受的語言,比如Accept-Language:en,zh。
Accept-Encoding:客戶端能夠理解的內容編碼方式——通常是某種壓縮算法——進行通知,通過內容協商的方式,服務端會選擇一個客戶端提議的方式,使用並在響應報文首部Content-Encoding中通知客戶端該選擇。需要注意的是,服務器端並不強制要求一定使用何種壓縮模式。
注意:有字符集限制,中文漢字可能需要URLEncoder.encode、URLDecoder.decode
實體主體:(Message Body)
實體主體是可選的,如GET時,實體主體爲空;Post請求時使用。
2、HTTP響應報文
與請求報文類似,只是請求行與狀態行的不同。在RFC中,請求行/狀態行稱爲起始行(Start Line),狀態值翻譯爲“短語”(reason-phrase)更好一些.
狀態碼:There are five values for the first digit:
o 1xx (Informational): The request was received, continuing process
o 2xx (Successful): The request was successfully received, understood, and accepted
o 3xx (Redirection): Further action needs to be taken in order to complete the request
o 4xx (Client Error): The request contains bad syntax or cannot be fulfilled
o 5xx (Server Error): The server failed to fulfill an apparently valid request
在網絡世界中,瀏覽器絕對不是唯一的 HTTP 客戶端工具,上面圖片中提到的 Postman等工具也是HTTP客戶端實現的一種,其實還有更多,只不過瀏覽器的使用最爲廣泛而已;明確不能將瀏覽器對HTTP協議的實現,當做了HTTP協議本身。
三、跨域問題
1、跨域問題的產生
跨域問題的產生原因:瀏覽器的同源策略。爲什麼要這個策略?安全問題,缺少易受到跨站腳本攻擊(XSS)、跨站請求僞造(CSRF)等攻擊。
所謂同源是指:協議、域名、端口相同。
參考:
注意:如果一個主機IP上有多個域名應用,在跨域問題上,域僅僅是通過“URL的首部”來識別而不會根據域名對應的IP地址是否相同來判斷。
2、解決方案
1、原生js的JSONP
由於同源策略,一般來說位於server1.example.com的網頁無法與 server2.example.com的服務器溝通,而HTML的 <script>元素是一個例外。利用 <script>元素的這個開放策略,網頁可以得到從其他來源動態產生的JSON數據,而這種使用模式就是所謂的 JSONP。
演示代碼:
服務端:
const url = require('url');
require('http').createServer((req, res) => {
const data = {
x: '隔壁超市薯片半價啦'
};
const callback = url.parse(req.url, true).query.callback
res.writeHead(200);
//回調原頁面上函數處理返回結果
res.end(`${callback}(${JSON.stringify(data)})`);
}).listen(3000, 'localhost');
console.log('服務器已啓動...');
瀏覽器端:http://local.dasouche-fin.net:8086/index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<script>
function jsonpCallback(data){
alert('跨域成功!'+data.x)
}
</script>
<script src="http://localhost:3000/?callback=jsonpCallback"></script>
</body>
</html>
缺點:
只能是get請求,因爲<script >是GET請求,不安全可能會遭受XSS攻擊。
2、jQuery的jsonp形式
jsonp技術跟ajax沒有任何關係,只不過jQuery將jsonp和ajax技術做了結合。
有很多種寫法,只列出一種:
$.ajax({
url:'http://外域.com/xxx.php',
dataType:"jsonp",
jsonp: "callback",
jsonpCallback:"ooo",
success:function(data){
console.log(data);
}
});
3、跨域資源共享 CORS
CORS(Cross-Origin Resource Sharing)是一種機制(W3C 標準),它使用額外的HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源服務器上的指定的資源,需要瀏覽器和服務端都支持才行。
3.1 簡單請求
使用下列方法之一:
GET
HEAD
POST
HTTP的頭信息不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
Content-Type 的值僅限於下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
請求中的任意XMLHttpRequestUpload 對象均沒有註冊任何事件監聽器;XMLHttpRequestUpload 對象可以使用 XMLHttpRequest.upload 屬性訪問。
請求中沒有使用 ReadableStream 對象。
簡單請求流程:
分別查看請求報文和響應報文:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]
3.2 非簡單請求
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。
1、非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱爲"預檢"請求(preflight)。
2、服務器收到"預檢"請求以後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以後,確認允許跨源請求,就可以做出迴應。
3、如果瀏覽器否定了"預檢"請求,會返回一個正常的HTTP迴應,但是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不同意預檢請求,因此觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制檯會打印出跨域報錯信息。
4、一旦服務器通過了"預檢"請求,以後每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin頭信息字段。
非簡單請求流程:
預檢請求的請求報文和響應報文:
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
預檢請求完成之後,發送實際請求報文和響應報文:
POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache
<?xml version="1.0"?><person><name>Arun</name></person>
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[Some GZIP'd payload]
3.3 CORS相關首部
Request headers:
- Origin:
指示了請求來自於哪個站點。該字段僅指示服務器名稱,並不包含任何路徑信息。
- Access-Control-Request-Method:
請求首部 Access-Control-Request-Method 出現於 preflight request (預檢請求)中,用於通知服務器在真正的請求中會採用哪種 HTTP 方法。因爲預檢請求所使用的方法總是 OPTIONS ,與實際請求所使用的方法不一樣,所以這個首部是必要的。
- Access-Control-Request-Headers
請求首部 Access-Control-Request-Headers 出現於 preflight request (預檢請求)中,用於通知服務器在真正的請求中會採用哪些請求首部。如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。
Response headers:
- Access-Control-Allow-Origin
響應頭指定了該響應的資源是否被允許與給定的origin共享。該字段是必須的。它的值要麼是請求時Origin字段的值,要麼是一個*,表示接受任意域名的請求。
- Access-Control-Allow-Methods
該字段必需,它的值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是爲了避免多次"預檢"請求。
- Access-Control-Allow-Credentials
CORS請求默認不發送Cookie和HTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials字段。
- Access-Control-Expose-Headers
該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers裏面指定。
- Access-Control-Max-Age
該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是20天(1728000秒),即允許緩存該條迴應1728000秒(即20天),在此期間,不用發出另一條預檢請求。
- Access-Control-Allow-Headers
如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。
CORS與JSONP的使用目的相同,但是比JSONP更強大。
JSONP只支持GET
請求,CORS支持所有類型的HTTP請求。JSONP的優勢在於支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。
4、Nginx反向代理
正向代理 和反向代理 ? ?
......
四、相關文檔
HTTP/1.1 RFC文檔:https://tools.ietf.org/html/rfc7230#section-2.7.1
Cookie RFC文檔:https://tools.ietf.org/html/rfc6265#section-4.2
XSS攻擊介紹文檔:https://tech.meituan.com/2018/09/27/fe-security.html
CSRF介紹文檔:https://tech.meituan.com/2018/10/11/fe-security-csrf.html
CORS文檔:https://www.w3.org/TR/cors/
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS