本章主要講什么(一句話)?
本章主要講解:重構(gòu)Node后臺加入Session支持及前臺跨域訪問配置
一但金、前言
上一章主要對項目的路由功能進(jìn)行完善:登錄路由层扶,注冊路由,發(fā)表文章路由诊笤,獲取用戶信息路由等,本章會繼續(xù)對于本項目的Node部分功能進(jìn)行完善巾陕。本將也將會是最后一章關(guān)于Node讨跟,Mongose相關(guān)技術(shù)及代碼的講解,下一章起鄙煤,將正式啟動angular2的前端部分许赃,敬請期待 :)
二、本章技術(shù)關(guān)健詞
Node馆类、MongoDB、Angular2弹谁、Mongoose乾巧、Route、Session预愤、跨域
三沟于、本章涉及核心技術(shù)點
四、內(nèi)容
4.1植康、Session
4.1.1旷太、為什么需要Session?
? ? ?cookie雖然很方便销睁,但是使用cookie有一個很大的弊端供璧,cookie中的所有數(shù)據(jù)在客戶端就可以被修改,數(shù)據(jù)非常容易被偽造冻记,那么一些重要的數(shù)據(jù)就不能存放在cookie中了睡毒,而且如果cookie中數(shù)據(jù)字段太多會影響傳輸效率。為了解決這些問題冗栗,就產(chǎn)生了session演顾,session中的數(shù)據(jù)是保留在服務(wù)器端的供搀。HTTP協(xié)議(http://www.w3.org/Protocols/)是“一次性單向”協(xié)議。服務(wù)端不能主動連接客戶端钠至,只能被動等待并答復(fù)客戶端請求葛虐。客戶端連接服務(wù)端棉钧,發(fā)出一個HTTP Request屿脐,服務(wù)端處理請求,并且返回一個HTTP Response給客戶端掰盘,本次HTTP Request-Response?Cycle結(jié)束摄悯。 我們看到,HTTP協(xié)議本身并不能支持服務(wù)端保存客戶端的狀態(tài)信息愧捕。于是奢驯,Web Server中引入了session的概念,用來保存客戶端的狀態(tài)信息次绘。
4.1.2瘪阁、什么是Session?
Session:在計算機(jī)中邮偎,尤其是在網(wǎng)絡(luò)應(yīng)用中管跺,稱為“會話”。Session直接翻譯成中文比較困難禾进,一般都譯成時域豁跑。在計算機(jī)專業(yè)術(shù)語中,Session是指一個終端用戶與交互系統(tǒng)進(jìn)行通信的時間間隔泻云,通常指從注冊進(jìn)入系統(tǒng)到注銷退出系統(tǒng)之間所經(jīng)過的時間艇拍。具體到Web中的Session指的就是用戶在瀏覽某個網(wǎng)站時,從進(jìn)入網(wǎng)站到瀏覽器關(guān)閉所經(jīng)過的這段時間宠纯,也就是用戶瀏覽這個網(wǎng)站所花費(fèi)的時間卸夕。因此從上述的定義中我們可以看到,Session實際上是一個特定的時間概念婆瓜。
4.1.3快集、Session的工作原理
一個session就是一系列某用戶和服務(wù)器間的通訊。服務(wù)器有能力分辨出不同的用戶廉白。一個session的建立是從一個用戶向服務(wù)器發(fā)第一個請求開始个初,而以用戶顯式結(jié)束或session超時為結(jié)束。
其工作原理是這樣的:
1.當(dāng)一個用戶向服務(wù)器發(fā)送第一個請求時猴蹂,服務(wù)器為其建立一個session勃黍,并為此session創(chuàng)建一個標(biāo)識號;
2.這個用戶隨后的所有請求都應(yīng)包括這個標(biāo)識號晕讲。服務(wù)器會校對這個標(biāo)識號以判斷請求屬于哪個session覆获。這種機(jī)制不使用IP作為標(biāo)識马澈,是因為很多機(jī)器是通過代理服務(wù)器方式上網(wǎng),沒法區(qū)分每一臺機(jī)器弄息。
形象比喻:
? ? ?這里用一個形象的比喻來解釋session的工作方式痊班。假設(shè)Web Server是一個商場的存包處,HTTP Request是一個顧客摹量,第一次來到存包處涤伐,管理員把顧客的物品存放在某一個柜子里面(這個柜子就相當(dāng)于Session),然后把一個號碼牌交給這個顧 客缨称,作為取包憑證(這個號碼牌就是Session?ID)凝果。顧客(HTTP Request)下一次來的時候,就要把號碼牌(Session ID)交給存包處(Web Server)的管理員睦尽。管理員根據(jù)號碼牌(Session ID)找到相應(yīng)的柜子(Session)器净,根據(jù)顧客(HTTP Request)的請求,Web Server可以取出当凡、更換山害、添加柜子(Session)中的物品,Web Server也可以讓顧客(HTTP Request)的號碼牌和號碼牌對應(yīng)的柜子(Session)失效沿量。顧客(HTTP Request)的忘性很大浪慌,管理員在顧客回去的時候(HTTP Response)都要重新提醒顧客記住自己的號碼牌(Session ID)。這樣朴则,顧客(HTTP Request)下次來的時候权纤,就又帶著號碼牌回來了。
4.1.4乌妒、express中的Session
express中操作session要用到express-session
(https://github.com/expressjs/session )這個模塊汹想,主要的方法就是session(options),其中options中包含可選參數(shù)芥被,主要有:
?name:設(shè)置cookie中,保存session的字段名稱坐榆,默認(rèn)為connect.sid拴魄。
?store:
session的存儲方式,默認(rèn)存放在內(nèi)存中席镀,也可以使用redis匹中,mongodb等。express生態(tài)中都有相應(yīng)模塊的支持豪诲。
?secret:通過設(shè)置的secret字符串顶捷,來計算hash值并放在cookie中,使產(chǎn)生的signedCookie防篡改屎篱。
?cookie:設(shè)置存放session id的cookie的相關(guān)選項服赎,默認(rèn)為
(default: { path: '/', httpOnly: true,secure: false, maxAge: null })
genid:產(chǎn)生一個新的session_id時葵蒂,所使用的函數(shù),默認(rèn)使用uid2這個npm包重虑。
?rolling:每個請求都重新設(shè)置一個cookie践付,默認(rèn)為false。
?resave:即使session沒有被修改缺厉,也保存session值永高,默認(rèn)為true。
express-session默認(rèn)使用內(nèi)存來存session提针,對于開發(fā)調(diào)試來說很方便
注意:在Express4.x中使用session時要額外安裝express-session包:
兩種安裝方式:
第一種:cnpm install express-session
第二種:package.json包中加入"express-session":"~1.14.1"
到其對應(yīng)目錄下執(zhí)行:npm install命令
var app = express();
var session= require('express-session');
app.listen(5000);
//按照上面的解釋命爬,設(shè)置session的可選參數(shù)
app.use(session({
secret: 'recommand 128 bytes random string', //建議使用128個字符的隨機(jī)字符串
cookie: { maxAge: 60 * 1000 }
}));
app.get('/', function (req, res) {
//檢查session中的isVisit字段
//如果存在則增加一次,否則為session設(shè)置isVisit字段辐脖,并初始化為1饲宛。
if(req.session.isVisit) {
req.session.isVisit++;
res.send('
第' + req.session.isVisit + '次來此頁面
');
} else {
req.session.isVisit = 1;
res.send("歡迎第一次來這里");
console.log(req.session);
}
});
4.1.4、項目中Session的代碼
? ? 基本上跟上述的配置大同小異揖曾,在這里不累述落萎,后面大家可以直接看代碼!此處略
4.2炭剪、跨域?
4.2.1练链、什么是跨域?
概念:只要協(xié)議奴拦、域名媒鼓、端口有任何一個不同,都被當(dāng)作是不同的域错妖。
URL ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 說明 ? ? ? ? ? ? ? ? ? ? ?是否允許通信
http://www.a.com/a.js
http://www.a.com/b.js ? ? ? ? ? ? ? 同一域名下 ? ? ? ? ? ? ? ? ? ? ? 允許
http://www.a.com/lab/a.js
http://www.a.com/script/b.js ? ? 同一域名下不同文件夾 ? ? 允許
http://www.a.com:8000/a.js
http://www.a.com/b.js ? ? ? ? ? ? ? ? ?同一域名绿鸣,不同端口 ? ? ?不允許
http://www.a.com/a.js
https://www.a.com/b.js ? ? ? ? ? ? ? ? ? 同一域名,不同協(xié)議 ? ? 不允許
http://www.a.com/a.js
http://70.32.92.74/b.js ? ? ? ? ? ? ? ? ? ? ? ?域名和域名對應(yīng)ip ? ? ? 不允許
http://www.a.com/a.js
http://script.a.com/b.js ? ? ? ? ? ? ? ? ? ? ? ?主域相同暂氯,子域不同 ? ? 不允許
http://www.a.com/a.js
http://a.com/b.js ? ? ? ? ? ? ? ? ? 同一域名潮模,不同二級域名(同上) ? ? ? 不允許(cookie這種情況下也不允許訪問)
http://www.cnblogs.com/a.js
http://www.a.com/b.js ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?不同域名 ? ? ? ? ? ? ? 不允許
4.2.2、為什么瀏覽器不支持跨域請求
主要的原因還是安全性痴施,防止CSRF攻擊擎厢。
CSRF是什么?
? ? CSRF(Cross-site request forgery)辣吃,中文名稱:跨站請求偽造动遭,也被稱為:one click attack/session riding,縮寫為:CSRF/XSRF神得。
CSRF可以做什么厘惦?
? ?你這可以這么理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發(fā)送惡意請求哩簿。CSRF能夠做的事情包括:以你名義發(fā)送郵件宵蕉,發(fā)消息酝静,盜取你的賬號,甚至于購買商品国裳,虛擬貨幣轉(zhuǎn)賬......造成的問題包括:個人隱私泄露以及財產(chǎn)安全形入。
4.2.3、跨域的幾種解決辦法
解決跨域訪問的辦法有N種缝左,最常用的為以下幾種:
1> document.domain + iframe? ? ? (只有在主域相同的時候才能使用該方法)
2> 動態(tài)創(chuàng)建script
3> location.hash + iframe
4> window.name + iframe
5> postMessage(HTML5中的XMLHttpRequest Level 2中的API)
6> CORS
7> JSONP
8> web sockets
要想了解以上8種的具體實現(xiàn)亿遂,可以參考此篇文章:http://blog.csdn.net/joyhen/article/details/21631833
但就像知道孔乙已的茴香豆的幾種寫法一樣,我認(rèn)為沒有太大的意義渺杉,我們更看重的怎么樣解決問題蛇数。
根據(jù)我個人的工作經(jīng)驗,在解決跨域時無外乎兩種是越,一種是客戶端想辦法耳舅,如使用JosnP(這個后面講Angular2時我會給大家演示),另一種在服務(wù)器端想辦法倚评,做配置 浦徊。相比較而言,第二種會配置更靈活天梧,功能更強(qiáng)大盔性,所以這里我主要給大家演示第二種方式。
4.2.4呢岗、Node中前端JS跨域配置代碼
好冕香,重點來了,直接上代碼:
打開index.js,找到以下代碼段:
var crypto = require('crypto'),
User = require('../models/user.js'),
Post = require('../models/post.js');
settings = require('../settings');
module.exports = function(app) {
? ? //此處我們將加入跨域代碼配置
}
在上述注釋處加入以下代碼:
app.all('*', function(req, res, next) {
res.header('Access-Control-Allow-Origin',settings.client);
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Credentials", "true"); //是否支持cookie跨域
next();
});
//跨域預(yù)檢查所有的get請求
app.all('/get/*', function(req, res, next) {
res.header('Access-Control-Allow-Methods', 'GET,OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200);
} else {
console.log(req.method);
next();
}
});
//跨域預(yù)檢查所有的post請求
app.all('/post/*', function(req, res, next) {
res.header('Access-Control-Allow-Methods', 'POST,OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200);
} else {
console.log(req.method);
next();
}
});
1. app.all('*', function(req, res, next) {后豫。悉尾。。}
app.all('/get/*', function(req, res, next) {挫酿。构眯。。}
app.all('/post/*', function(req, res, next) {早龟。惫霸。。}
注意:第一行 * 代表拄衰,所有前端路由請求它褪,必須經(jīng)過此“過濾器”攔截處理
/get/*饵骨,代表所有以get打頭的路由請求翘悉,必須經(jīng)過此“過濾器”攔截處理,如:http://localhost:8800/get/user?name=zzz ?居触,這個請求就會被 此“過濾器”攔截
/post/*妖混,代表所有以post打頭的路由請求老赤,必須經(jīng)過此“過濾器”攔截處理,如:http://localhost:8800/post/reg 制市,這個請求就會被 此“過濾器”攔截
2. res.header('Access-Control-Allow-Origin',settings.client);
這種代碼的作用是:只有當(dāng)目標(biāo)頁面的response中抬旺,包含了Access-Control-Allow-Origin這個header,并且它的值里有我們自己的域名時祥楣,瀏覽器才允許我們拿到它頁面的數(shù)據(jù)進(jìn)行下一步處理开财,即設(shè)置 “同源策略“, 如果它的值設(shè)為*,則表示誰都可以用:Access-Control-Allow-Origin: *,但一般我們不會這么干误褪,出于安全性考慮责鳍,最好還是設(shè)定你允許訪問的網(wǎng)站
這名代碼相當(dāng)于設(shè)置了:
Access-Control-Allow-Origin : http://localhost:3000
注意settings.client的配置:
settings.js
module.exports = {
cookieSecret: 'myblog',
db: 'blog',
host: 'localhost',
port: 27017,
client:'http://localhost:3000'? //用于設(shè)置跨域
};
3. res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
OPTIONS請求頭部中會包含以下頭部:Origin、Access-Control-Request-Method兽间、Access-Control-Request-Headers历葛,發(fā)送這個請求后,服務(wù)器可以設(shè)置如下頭部與瀏覽器溝通來判斷是否允許這個請求嘀略。yourHeaderFeild 這個表示你可以自定義與瀏覽器客戶端溝通的信息恤溶。
4. res.setHeader("Access-Control-Max-Age", "3600");
Access-Control-Max-Age: 3600?// 表明在3600秒內(nèi),不需要再發(fā)送預(yù)檢驗請求帜羊,可以緩存該結(jié)果,即CORS緩存配置
5. res.setHeader("Access-Control-Allow-Credentials", "true"); //是否支持cookie跨域,ture代表支持咒程,注意這個配置很重要,不然后面我們和Angular2 Http請求時逮壁,發(fā)現(xiàn)不配置此項無法進(jìn)行服務(wù)端的Session操作
6. Access-Control-Allow-Methods : 表明它允許GET孵坚、POST、PUT窥淆、DELETE的外域請求
注意app.all('/get/*'卖宠。。忧饭。)與 app.all('/post/*',扛伍。。词裤。)里面的內(nèi)容的區(qū)別刺洒,這樣做的目的,主要還是從更高效和更安全的角度去考慮的
7. 注意吼砂,上面各個 ” 過濾器“里的 next() 不可省
五逆航、后述
? ? ?好了,項目到了這里渔肩,關(guān)于Node與MongoDB操作部分內(nèi)容因俐,已經(jīng)基本全部結(jié)束,希望大家能夠有所收獲,后繼我們將繼續(xù)Angular2部分抹剩,因近期工作比較忙撑帖,所以更新文章有些慢,大家見諒澳眷,大家支持的話胡嘿,可以給我點贊,評論钳踊,轉(zhuǎn)發(fā)衷敌,您的支持將會是持續(xù)下去的動力!
本章代碼下載:http://pan.baidu.com/s/1dFf22yt
下章劇透:
《項目實戰(zhàn):基于Angular2+Mongodb+Node技術(shù)實現(xiàn)的多用戶博客系統(tǒng)教程(11)》
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?-- ?Angular2前臺框架搭建