- https 4次握手
- c端請(qǐng)求,s端響應(yīng)并提供證書(包括公鑰、證書信息、頒發(fā)機(jī)構(gòu)、過(guò)期時(shí)間等,s端私鑰加密生成);
- c端校驗(yàn)接收數(shù)據(jù)(主要是校驗(yàn)公鑰確實(shí)是s端發(fā)的,見(jiàn)數(shù)字證書部分)后,生成pre-master-secret并使用s端發(fā)過(guò)來(lái)的公鑰加密;
- s端接收到后使用私鑰解密,并最終通過(guò)某種算法生成master-secret;
- 后續(xù)的通信中s和c端均使用這個(gè)master-secret生成的密鑰。
(其中兩個(gè)secret是為了提高隨機(jī)性,減少隨機(jī)數(shù)被猜出的風(fēng)險(xiǎn))
- 數(shù)字證書發(fā)布過(guò)程
- 服務(wù)器生成一對(duì)密鑰,私鑰自己留著,公鑰交給數(shù)字證書認(rèn)證機(jī)構(gòu)(CA) 。
- CA進(jìn)行審核,并用自己的私鑰對(duì)服務(wù)器的公鑰進(jìn)行簽名。
- 客戶端訪問(wèn)服務(wù)器時(shí),從CA獲取證書(包括服務(wù)器端公鑰),用CA的公鑰對(duì)簽名的證書進(jìn)行驗(yàn)證,一致則說(shuō)明該服務(wù)器公鑰確實(shí)是CA頒發(fā)的。
- https涉及的加密算法
- 非對(duì)稱加密(非對(duì)稱加密算法用于握手過(guò)程中的加密生成的密碼)
RSA,DSA/DSS - 對(duì)稱加密(對(duì)真正傳輸?shù)臄?shù)據(jù)進(jìn)行加密):AES,RC4,3DES
- HASH(用于驗(yàn)證數(shù)據(jù)的完整性,是否被篡改等):MD5,SHA1,SHA256
非對(duì)稱加密生成密碼是整個(gè)加密過(guò)程的關(guān)鍵,非對(duì)稱加密會(huì)生成公鑰和私鑰,公鑰負(fù)責(zé)數(shù)據(jù)加密,可以隨意傳輸。私鑰負(fù)責(zé)解密。
- https如何優(yōu)化
- http使用TCP 三次握手建立連接,客戶端和服務(wù)器需要交換3個(gè)包,https除了 TCP 的三個(gè)包,還要加上 SSL握手需要的9個(gè)包,所以一共是12個(gè)包。
- https的開(kāi)銷主要是SSL部分(尤其是session建立階段)。主要是網(wǎng)絡(luò)延時(shí)和SSL本身加解密的開(kāi)銷(服務(wù)器根據(jù)客戶端的信息確定是否需要生成新的主密鑰;服務(wù)器回復(fù)該主密鑰,并返回給客戶端一個(gè)用主密鑰認(rèn)證的信息;服務(wù)器向客戶端請(qǐng)求數(shù)字簽名和公開(kāi)密鑰)。
- 可以通過(guò)負(fù)載均衡的SSL termination proxy來(lái)優(yōu)化,Web 服務(wù)放在SSL termination proxy之后。SSL termination proxy可以是基于硬件的,譬如F5;也可以軟件的,如Nginx(維基百科用的)。
- 另外也可以通過(guò)SSL session或SSL ticket復(fù)用來(lái)優(yōu)化。session ID就是每一次對(duì)話都有一個(gè)編號(hào)(session ID)。只要客戶端給出這個(gè)編號(hào),且服務(wù)器有這個(gè)編號(hào)的記錄,雙方就可以重新使用已有的"對(duì)話密鑰",但session ID 往往只保留在一臺(tái)服務(wù)器上;而session ticket類似,但是加密的,只有服務(wù)器才能解密,其中包括本次對(duì)話的主要信息,比如對(duì)話密鑰和加密方法。當(dāng)服務(wù)器收到 session ticket 以后,解密后就不必重新生成對(duì)話密鑰了,支持負(fù)載均衡。
- (Heartbleed就是RFC 在2012年提出了 RFC6520 TLS 的心跳擴(kuò)展用于優(yōu)化SSL所引起的漏洞。原本希望通過(guò)在客戶端和服務(wù)器之間來(lái)回發(fā)送心跳的請(qǐng)求和應(yīng)答,保活 TLS session,減少重建 TLS的session的性能開(kāi)銷,但未對(duì)心跳請(qǐng)求進(jìn)行長(zhǎng)度檢查,導(dǎo)致客戶端可以讀服務(wù)器內(nèi)存的數(shù)據(jù)。)
- http2特性
- http的語(yǔ)義、頭部、狀態(tài)碼、方法等不受影響
- 服務(wù)器端推送消息
- 傳輸二進(jìn)制幀而非文本(亂序發(fā)幀并收到后重組)
- 僅在一個(gè)TCP連接中并行處理(通過(guò)請(qǐng)求優(yōu)先級(jí)控制)
- 壓縮頭部信息減小開(kāi)銷
- 在客戶端和服務(wù)器端使用“首部表”來(lái)跟蹤和存儲(chǔ)之前發(fā)送的鍵-值對(duì),對(duì)于相同的數(shù)據(jù),不再通過(guò)每次請(qǐng)求和響應(yīng)發(fā)送
- 從輸入url到渲染的整個(gè)過(guò)程
- 客戶端DNS尋址(瀏覽器DNS緩存、操作系統(tǒng)DNS緩存、本地域名服務(wù)器、向根域名服務(wù)器發(fā)送迭代請(qǐng)求)獲得目標(biāo)IP(有可能是CDN的IP)。
- TCP或UDP連接建立。
- 數(shù)據(jù)經(jīng)過(guò)路由器到運(yùn)營(yíng)商網(wǎng)絡(luò),經(jīng)過(guò)層層轉(zhuǎn)發(fā)到達(dá)服務(wù)器。
- 數(shù)據(jù)經(jīng)過(guò)負(fù)載均衡(F5,LVS,反向代理)
- 數(shù)據(jù)到達(dá)web server,經(jīng)過(guò)后端業(yè)務(wù)邏輯
- 如果采用SSR(服務(wù)器端渲染),則后端調(diào)用數(shù)據(jù)(數(shù)據(jù)庫(kù)等)生成應(yīng)用代碼,如果是前后端分離架構(gòu),則數(shù)據(jù)和網(wǎng)頁(yè)文件分別返回。
- 數(shù)據(jù)通過(guò)網(wǎng)絡(luò)回到客戶端(內(nèi)網(wǎng)NAT穿透)
- 數(shù)據(jù)到達(dá)瀏覽器,如有g(shù)zip壓縮先解壓,開(kāi)始編碼(這里要注意是utf-8還是gbk)
- html被解析(DOM樹(shù)、CSSDOM樹(shù)、呈現(xiàn)樹(shù)等,見(jiàn)上面問(wèn)題),加載外部資源,文字、圖片渲染至屏幕。
- TCP三次握手,四次揮手過(guò)程
-
三次握手
三次握手 -
四次揮手
四次揮手
- CDN加速原理
- 用戶向?yàn)g覽器輸入www.web.com這個(gè)域名,瀏覽器第一次發(fā)現(xiàn)本地沒(méi)有DNS緩存,則向根域名服務(wù)器發(fā)起請(qǐng)求。
- 根域名服務(wù)器設(shè)置了CNAME,指向了www.cdn.com,即CDN網(wǎng)絡(luò)中的智能DNS負(fù)載均衡系統(tǒng)。
- 智能DNS負(fù)載均衡系統(tǒng)解析域名,把對(duì)用戶響應(yīng)速度最快的IP節(jié)點(diǎn)返回給用戶。
- 用戶向該IP節(jié)點(diǎn)(CDN服務(wù)器)發(fā)出請(qǐng)求。
- 由于是第一次訪問(wèn),CDN服務(wù)器會(huì)向原web站點(diǎn)請(qǐng)求,并緩存內(nèi)容。
- 請(qǐng)求結(jié)果發(fā)給用戶。
- 靜態(tài)資源優(yōu)化
- 配置超長(zhǎng)時(shí)間的本地緩存(cache-control/expires) —— 節(jié)省帶寬,提高性能
- 采用hash值作為緩存更新依據(jù) —— 精確的緩存控制
- 靜態(tài)資源CDN部署 —— 優(yōu)化網(wǎng)絡(luò)請(qǐng)求
- 更新資源發(fā)布路徑實(shí)現(xiàn)非覆蓋式發(fā)布 —— 平滑升級(jí)
- 為何很多網(wǎng)站靜態(tài)資源會(huì)使用獨(dú)立的域名
- 靜態(tài)資源的http請(qǐng)求中不會(huì)攜帶無(wú)用的cookie。
- 動(dòng)靜分離更有利于CDN。
- http對(duì)同一個(gè)域名的同時(shí)下載線程數(shù)是有限制的,只要是為了優(yōu)化下載速度,防止一個(gè)域名下太多下載線程,每個(gè)瀏覽器的限制不同。
- 方便復(fù)用,放在另一個(gè)服務(wù)器上,可以方便全局內(nèi)其他產(chǎn)品的使用,比如說(shuō)taobao.com的js tmall也可以用,且客戶端可緩存。
- 跨域解決
- JSONP:僅支持get方法。原理是通過(guò)標(biāo)簽的src屬性引用的文件可以跨域,那么傳回一個(gè)腳本,將數(shù)據(jù)包在腳本中回調(diào)函數(shù)里并動(dòng)態(tài)執(zhí)行就可以拿到函數(shù)中的數(shù)據(jù)。
- CORS:后端配置Access-Control-Allow-Origin(IE10+)。注意包括簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求。
- 代理:正向反向都可(nginx)
- CORS的簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求
方法為HEAD,GET,POST之一,且headers只包括Accept, Accept-Language, Content-Language, Last-Event-ID, Content-Type(值為application/x-www-form-urlencoded, multipart/form-data, text/plain之一)的請(qǐng)求為簡(jiǎn)單請(qǐng)求。
- 簡(jiǎn)單請(qǐng)求瀏覽器直接發(fā)出,只添加一個(gè)origin頭,服務(wù)器若允許該origin,則在響應(yīng)頭加上Access-Control-Allow-Origin: origin的值表示允許跨域。
Access-Control-Allow-Credentials控制服務(wù)器是否接受Cookies,如果不為true但是傳了Cookies,則前端會(huì)報(bào)異常(注意,如果接受Cookies則origin的值不能為星號(hào))。
Access-Control-Expose-Headers用來(lái)控制前端可以拿哪些響應(yīng)頭的header。 - 復(fù)雜請(qǐng)求瀏覽器會(huì)先發(fā)一個(gè)OPTION方法的preflight請(qǐng)求。預(yù)檢請(qǐng)求中Access-Control-Request-Method用來(lái)標(biāo)志此次復(fù)雜請(qǐng)求的方法,Access-Control-Request-Headers表明復(fù)雜請(qǐng)求額外發(fā)送的header字段。服務(wù)器檢查origin及method、header后,如果接受則類似簡(jiǎn)單請(qǐng)求發(fā)送響應(yīng),僅多一個(gè)Access-Control-Max-Age來(lái)標(biāo)志預(yù)檢的有效期。
- 自己寫一個(gè)jsonp的實(shí)現(xiàn)
var JSONP = {
now: function() { // 獲取當(dāng)前時(shí)間戳
return (new Date()).getTime();
},
rand: function() { // 獲取隨機(jī)數(shù)
return Math.random().toString().substr(2);
},
removeElem: function(elem) {
var parent = elem.parentNode;
if(parent && parent.nodeType !== 11) {
parent.removeChild(elem);
}
},
parseData: function(data) { // url組裝
var ret = "";
if(typeof data === "string") {
ret = data;
}else if(typeof data === "object") {
for(var key in data) {
ret += "&" + key + "=" + encodeURIComponent(data[key]);
}
}
ret += "&_time=" + this.now();// 加個(gè)時(shí)間戳,防止緩存
ret = ret.substr(1);
return ret;
},
getJSON: function(url, data, func) { // 函數(shù)名稱
var name;
url = url + (url.indexOf("?") === -1 ? "?" : "&") + this.parseData(data); // 拼裝url
var match = /callback=(\w+)/.exec(url); // 檢測(cè)callback的函數(shù)名是否已經(jīng)定義
if(match && match[1]) {
name = match[1];
} else {
// 如果未定義函數(shù)名的話隨機(jī)成一個(gè)函數(shù)名
// 隨機(jī)生成的函數(shù)名通過(guò)時(shí)間戳拼16位隨機(jī)數(shù)的方式,重名的概率基本為0
// 如:jsonp_1355750852040_8260732076596469
name = "jsonp_" + this.now() + '_' + this.rand();
url = url.replace("callback=?", "callback="+name);// 把callback中的?替換成函數(shù)名
url = url.replace("callback=%3F", "callback="+name);// 處理?被encode的情況
}
var script = document.createElement("script"); // 創(chuàng)建一個(gè)script元素
script.type = "text/javascript";
script.src = url; // 設(shè)置要遠(yuǎn)程的url
script.id = "id_" + name; // 設(shè)置id,為了后面可以刪除這個(gè)元素
// 把傳進(jìn)來(lái)的函數(shù)重新組裝,并把它設(shè)置為全局函數(shù),遠(yuǎn)程就是調(diào)用這個(gè)函數(shù)
window[name] = function(json) {
window[name] = undefined; // 執(zhí)行這個(gè)函數(shù)后,要銷毀這個(gè)函數(shù)
var elem = document.getElementById("id_" + name); // 獲取這個(gè)script的元素
JSONP.removeElem(elem); // 刪除head里面插入的script,這三步都是為了不影響污染整個(gè)DOM啊
func(json); // 執(zhí)行傳入的的函數(shù)
};
var head = document.getElementsByTagName("head"); // 在head里面插入script元素
if(head && head[0]) {
head[0].appendChild(script);
}
}
};
//調(diào)用的方法跟jQuery基本一樣。如:
var data = {
from: "北京",
count: 27,
output: "json",
callback: "?"
}
JSONP.getJSON("http://api.qunar.com/cdnWebservices.jcp", data, function(json) {console.log(json)});
- 原生ajax
<script>
// 1.獲得ajax
if (window.XMLHttpRequest) { //查看當(dāng)前瀏覽器XMLHttpRequest是否是全局變量
var oAjax = new XMLHttpResquest();
} else {
var oAjax = new ActiveXObject('Microsoft.XMLHTTP'); //IE6,傳入微軟參數(shù)
}
// 2.打開(kāi)地址
switch (json.type.toLowerCase()) {
case 'get':
oAjax.open('GET', json.url + '?' + jsonToURL(json.data), true); // 提交方式(大寫),url,是否異步
// 3.發(fā)送GET數(shù)據(jù)
oAjax.send();
break;
case 'post':
oAjax.open('POST', json.url, true);
oAjax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
oAjax.send(jsonToURL(json.data));
// 3.發(fā)送POST數(shù)據(jù)
break;
}
// 4.接收數(shù)據(jù)
oAjax.onreadystatechange = function() { //監(jiān)控狀態(tài)
if (oAjax.readyState == 4) {
json.complete && json.complete();
if (oAjax.status >= 200 && oAjax.status < 300 ||
oAjax.status == 304) {
json.success && json.success(oAjax.responseText); //執(zhí)行成功的回調(diào)函數(shù), responseText為響應(yīng)內(nèi)容
} else {
json.error && json.error(oAjax.status); //執(zhí)行失敗的回調(diào)函數(shù)
}
}
};
</script>

