Angular.js與node.js項目里用cookie校驗賬戶登錄詳解
前言
最近的新項目中,用戶登錄需要采用cookie來記住用戶,校驗身份。所以本文就把實現(xiàn)的過程總結出來分享給大家,需要的朋友們可以參考學習。
在header中攜帶authId登錄
在之前老的項目里,沒有采用cookie來記錄用戶登錄狀態(tài),而是在請求的header中攜帶一個身份標識來校驗,大致方案如下:
- 客戶端使用post請求提交user、password給服務端進行登錄操作;
- 服務端校驗用戶是否合法,如果合法將產(chǎn)生一個唯一的身份標識authId,返回給客戶端,客戶端將此authId存放本地(如localStorage);
- 客戶端在每次需要校驗身份的請求中,往header中加入這個authId;
- 服務端檢測當前的authId是否有效,有效則表示當前用戶合法,允許操作;
- 客戶端用戶登出的時候,發(fā)送一個delete請求,告訴服務端用戶注銷,同時刪除本地的authId信息;
- 服務端收到注銷請求后,刪除當前的authId數(shù)據(jù)。
上面的方案,如果其他客戶端知道了這個authId后,可以在其他客戶端模擬身份,不安全,因此棄用。
用cookie記住用戶
新項目中,將采用此文即將介紹的方案–利用cookie來記住用戶。主要流程是:
- 客戶端使用post請求提交user、password給服務端進行登錄操作;
- 服務端校驗用戶是否合法,如果合法將產(chǎn)生一個唯一的身份標識authId,以cookie的形式返回給客戶端;
- 客戶端再次請求服務端時,會攜帶此前已經(jīng)拿到的cookie給服務端,服務端校驗是否合法,合法則可以繼續(xù)操作;
- 客戶端用戶登出的時候,發(fā)送一個delete請求,告訴服務端用戶注銷,服務端刪除登錄標識。
整個過程可以用下面這張圖簡單表示:

前臺用angular搭建單頁客戶端應用,后臺用node搭建服務器,數(shù)據(jù)存放在mongodb中,這三個技術及cookie基礎知識本文不做介紹,感興趣的同學可以自行了解。
以下的代碼都是最簡單的get/post請求,但也是最核心的部分,其他有關登錄的繁瑣操作,感興趣的同學可以自行補充。
從開始–>結束,遇到的問題
首先,我用的是最基礎的post請求,服務端也只是簡單的返回數(shù)據(jù),部分簡單但比較核心的代碼如下:
// client
$http({
method : 'POST',
url : 'http://127.0.0.1:8888/rest/user',
data : {name: 'xxx',password:'***'}
}).success(function (data) {
console.log('login success,data is:'+data);
}).error(function (data) {
console.log('login error');
}).then(function () {
console.log(arguments);
});
// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();
查看chrome調試,發(fā)現(xiàn)雖然服務端的cookie推過來了,但整體出了問題,提示如下:

XMLHttpRequest cannot load http://127.0.0.1:8888/rest/user. 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:62427' is therefore not allowed access.
分析問題后,發(fā)現(xiàn)原因是來自客戶端的請求不能跨域訪問服務端的請求,請求的資源header中沒有攜帶允許跨越請求的信息。根據(jù)這個提示,我們把服務端的代碼稍加改進后,如下:
// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');
// 添加允許跨越的頭信息
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();
解釋下上面代碼什么意思,第一句主要是允許來自任何域的請求訪問,第二句是允許哪些類型的請求訪問。加上后再次運行,提示如下:
XMLHttpRequest
cannot load http://127.0.0.1:8888/rest/user. Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
原因是來自客戶端的請求中,Content-Type頭字段,在服務端的響應信息的頭中,沒有攜帶,再次修改代碼如下:
// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');
// 添加允許跨越的頭信息
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
// 添加支持Content-Type允許的頭信息
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();
再次運行代碼,發(fā)現(xiàn)沒有錯誤提示,但是當我們再次請求服務器時,發(fā)現(xiàn)客戶端的請求并沒有攜帶cookie信息,這顯然不是我們想要的效果:

在查閱了一段時間后了解到,客戶端是會默認攜帶cookie給服務端的,但是當客戶端的請求是跨域請求時,由于跨域請求本身就有風險,而攜帶給cookie同樣有風險。
因此在進行跨域訪問時,客戶端不會將服務端返回的cookie攜帶。此時,我們需要同時在客戶端和服務端都設置“withCredentials”為true,代碼如下:
// client
$http({
method : 'POST',
url : 'http://127.0.0.1:8888/rest/user',
withCredentials: true
data : {name: 'xxx',password:'***'}
}).success(function (data) {
console.log('login success,data is:'+data);
}).error(function (data) {
console.log('login error');
}).then(function () {
console.log(arguments);
});
// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');
// 添加允許跨越的頭信息
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
// 添加支持Content-Type允許的頭信息
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// 設置已攜帶憑證為true
//res.setHeader('Access-Control-Allow-Credentials', true);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();
運行后,發(fā)現(xiàn)又有錯誤提示,如下:

XMLHttpRequest cannot load http://127.0.0.1:8888/rest/user. Response to preflight request doesn't pass access control check: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'http://localhost:62427' is therefore not allowed access.
分析錯誤后發(fā)現(xiàn),原因是當設置了已攜帶憑證參數(shù)為true時,允許跨域請求的源不能設置為泛型的“*”,因此我們再次修改代碼如下:(最終代碼)
// client
$http({
method : 'POST',
url : 'http://127.0.0.1:8888/rest/user',
withCredentials: true
data : {name: 'xxx',password:'***'}
}).success(function (data) {
console.log('login success,data is:'+data);
}).error(function (data) {
console.log('login error');
}).then(function () {
console.log(arguments);
});
// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');
// 添加允許跨越的頭信息
// res.setHeader('Access-Control-Allow-Origin', '*');
// 用當前的客戶端origin來取代泛型的“*”
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:62427');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
// 添加支持Content-Type允許的頭信息
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// 設置已攜帶憑證為true
res.setHeader('Access-Control-Allow-Credentials', true);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();
此時,第一次請求服務端時,服務端返回cookie信息,以后每次客戶端請求服務端,客戶端的header中都會攜帶cookie信息,效果如下圖:

最后
以上就是在使用cookie記住用戶身份時遇到的一些問題及簡單解決方法,一般在angular應用中,可能使用較多的是resoure進行http通信,此時只要在GET/POST/PUT/DELETE等請求的參數(shù)中,將“withCredentials”設置為true即可。希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
相關文章
Angularjs渲染的 using 指令的星級評分系統(tǒng)示例
本篇文章主要介紹了Angularjs渲染的 using 指令的星級評分系統(tǒng)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
詳解為Angular.js內(nèi)置$http服務添加攔截器的方法
所謂攔截器就是在目標達到目的地之前對其進行處理以便處理結果更加符合我們的預期。Angular的$http攔截器是通過$httpProvider.interceptors數(shù)組定義的一組攔截器,每個攔截器都是實現(xiàn)了某些特定方法的Factory。本文就介紹了為Angular.js內(nèi)置$http服務添加攔截器的方法。2016-12-12
angularjs實現(xiàn)上拉加載和下拉刷新數(shù)據(jù)功能
本篇文章主要介紹了angularjs實現(xiàn)上拉加載和下拉刷新數(shù)據(jù)功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
AngularJS通過$location獲取及改變當前頁面的URL
本篇將介紹AngularJS中的$location服務的基本用法,$location服務的主要作用是用于獲取當前url以及改變當前的url,并且存入歷史記錄。本文通過示例代碼介紹的很詳細,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-09-09
angular基于ng-alain定義自己的select組件示例
這篇文章主要介紹了angular基于ng-alain定義自己的select組件示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02

