您好,登錄后才能下訂單哦!
這篇文章給大家介紹怎么在Node.js中利用cookie保持登錄,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
1. 使用express創建應用
就下面的命令序列:
express LoginDemo cd LoginDemo npm install
2. 登錄頁面
登錄頁面的jade模板為login.jade,內容如下:
doctype html html head meta(charset='UTF-8') title 登錄 link(rel='stylesheet', href='/stylesheets/login.css') body .form-container p.form-header 登錄 form(action='login', method='POST', align='center') table tr td label(for='user') 賬號: td input#user(type='text', name='login_username') tr td label(for='pwd') 密碼: td input#pwd(type='password', name='login_password') tr td(colspan='2', align='right') input(type='submit', value='登錄') p #{msg}
login.jade放在views目錄下。我在login.jade里硬編碼了漢字,注意文件用UTF-8編碼。
這個模板的最后是一條動態消息,用于顯示登錄錯誤信息,msg變量由應用程序傳入。
我給login頁面寫了個簡單的CSS,login.css文件,內容如下:
form { margin: 12px; } a { color: #00B7FF; } div.form-container { display: inline-block; border: 6px solid steelblue; width: 280px; border-radius: 10px; margin: 12px; } p.form-header { margin: 0px; font: 24px bold; color: white; background: steelblue; text-align: center; } input[type=submit]{ font: 18px bold; width: 120px; margin-left: 12px; }
請把login.css放在public/stylesheets目錄下。
3. profile頁面
登錄成功后會顯示配置頁面,profile.jade頁面內容:
doctype html html head meta(charset='UTF-8') title= title body p #{msg} p #{lastTime} p a(href='/logout') 退出
profile.jade放在views目錄下。profile頁面顯示一條登錄成功的消息,還顯示上次登錄時間,最后提供了一個退出鏈接。
4. app.js改動
我改動了app.js,以便用戶在沒有登錄時訪問網站自動跳轉到login頁面。新的app.js內容如下:
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var users = require('./routes/users'); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.all('*', users.requireAuthentication); app.use('/', users); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handlers // development error handler // will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); }); } // production error handler // no stacktraces leaked to user app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); }); module.exports = app;
5. users.js
我修改了users.js,把認證、登錄、登出等邏輯放在里面,首先要把users.js轉為UTF-8編碼(sorry,硬編碼了漢字哈)。內容:
var express = require('express'); var router = express.Router(); var crypto = require('crypto'); function hashPW(userName, pwd){ var hash = crypto.createHash('md5'); hash.update(userName + pwd); return hash.digest('hex'); } // just for tutorial, it's bad really var userdb = [ { userName: "admin", hash: hashPW("admin", "123456"), last: "" }, { userName: "foruok", hash: hashPW("foruok", "888888"), last: "" } ]; function getLastLoginTime(userName){ for(var i = 0; i < userdb.length; ++i){ var user = userdb[i]; if(userName === user.userName){ return user.last; } } return ""; } function updateLastLoginTime(userName){ for(var i = 0; i < userdb.length; ++i){ var user = userdb[i]; if(userName === user.userName){ user.last = Date().toString(); return; } } } function authenticate(userName, hash){ for(var i = 0; i < userdb.length; ++i){ var user = userdb[i]; if(userName === user.userName){ if(hash === user.hash){ return 0; }else{ return 1; } } } return 2; } function isLogined(req){ if(req.cookies["account"] != null){ var account = req.cookies["account"]; var user = account.account; var hash = account.hash; if(authenticate(user, hash)==0){ console.log(req.cookies.account.account + " had logined."); return true; } } return false; }; router.requireAuthentication = function(req, res, next){ if(req.path == "/login"){ next(); return; } if(req.cookies["account"] != null){ var account = req.cookies["account"]; var user = account.account; var hash = account.hash; if(authenticate(user, hash)==0){ console.log(req.cookies.account.account + " had logined."); next(); return; } } console.log("not login, redirect to /login"); res.redirect('/login?'+Date.now()); }; router.post('/login', function(req, res, next){ var userName = req.body.login_username; var hash = hashPW(userName, req.body.login_password); console.log("login_username - " + userName + " password - " + req.body.login_password + " hash - " + hash); switch(authenticate(userName, hash)){ case 0: //success var lastTime = getLastLoginTime(userName); updateLastLoginTime(userName); console.log("login ok, last - " + lastTime); res.cookie("account", {account: userName, hash: hash, last: lastTime}, {maxAge: 60000}); res.redirect('/profile?'+Date.now()); console.log("after redirect"); break; case 1: //password error console.log("password error"); res.render('login', {msg:"密碼錯誤"}); break; case 2: //user not found console.log("user not found"); res.render('login', {msg:"用戶名不存在"}); break; } }); router.get('/login', function(req, res, next){ console.log("cookies:"); console.log(req.cookies); if(isLogined(req)){ res.redirect('/profile?'+Date.now()); }else{ res.render('login'); } }); router.get('/logout', function(req, res, next){ res.clearCookie("account"); res.redirect('/login?'+Date.now()); }); router.get('/profile', function(req, res, next){ res.render('profile',{ msg:"您登錄為:"+req.cookies["account"].account, title:"登錄成功", lastTime:"上次登錄:"+req.cookies["account"].last }); }); module.exports = router;
如你所見,我內置了兩個賬號,admin和foruok,登錄時就驗證這兩個賬號,不對就報錯。
好了,執行“npm start”,然后在瀏覽器里打開“http://localhost:3000”,可以看到下面的效果:
折騰幾次,登錄,退出,再次登錄,效果如下:
好啦,這就是這個示例的效果。接下來我們來解釋一下用到概念和部分代碼。
處理POST正文數據
我們在示例中使用了HTML表單來接收用戶名和密碼,當input元素的類型為submit時,點擊它,瀏覽器會把表單內的數據按一定的格式組織之后編碼進body,POST到指定的服務器地址。用戶名和密碼,在服務器端,可以通過HTML元素的名字屬性的值找出來。
服務器解析表單數據這一過程,我們不用擔心,用了express的body-parser中間件,它會幫我們做這件事,只要做簡單的配置即可。而且這些配置代碼,express generator都幫我們完成了,如下:
//加載body-parser模塊 var bodyParser = require('body-parser'); ... //應用中間件 app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false }));
我們處理/login路徑上的POST請求的代碼在users.js里,從“router.post(‘/login'…”開始(94行,要是markdown能自動給代碼插入行號就好了)。引用登錄表單內的用戶名的代碼如下:
var userName = req.body.login_username;
注意到了吧,express.Request對象req內有解析好的body,我們使用login_username來訪問用戶名。而login_username就是我們在HTML里的input元素的name屬性的值。就這么關聯的。password也類似。
cookie
cookie,按我的理解,就是服務器發給瀏覽器的一張門票,要訪問服務器內容,可以憑票入場,享受某種服務。服務器可以在門票上記錄一些信息,從技術角度講,想記啥記啥。當瀏覽器訪問服務器時,HTTP頭部把cookie信息帶到服務器,服務器解析出來,校驗當時記錄在cookie里的信息。
HTTP協議本身是無狀態的,而應用服務器往往想保存一些狀態,cookie應運而生,由服務器頒發,通過HTTP頭部傳給瀏覽器,瀏覽器保存到本地。后續訪問服務器時再通過HTTP頭部傳遞給服務器。這樣的交互,服務器就可以在cookie里記錄一些用戶相關的信息,比如是否登錄了,賬號了等等,然后就可以根據這些信息做一些動作,比如我們示例中的持久登錄的實現,就利用了cookie。還有一些電子商務網站,實現購物車時也可能用到cookie。
cookie存儲的是一些key-value對。在express里,Request和Response都有cookie相關的方法。Request實例req的cookies屬性,保存了解析出的cookie,如果瀏覽器沒發送cookie,那這個cookies對象就是一個空對象。
express有個插件,cookie-parser,可以幫助我們解析cookie。express生成的app.js已經自動為我們配置好了。相關代碼:
var cookieParser = require('cookie-parser'); ... app.use(cookieParser());
express的Response對象有一個cookie方法,可以回寫給瀏覽器一個cookie。
下面的代碼發送了一個名字叫做“account”的cookie,這個cookie的值是一個對象,對象內有三個屬性。
復制代碼 代碼如下:
res.cookie("account", {account: userName, hash: hash, last: lastTime}, {maxAge: 60000});
res.cookie()方法原型如下:
res.cookie(name, value [, options])
文檔在這里:http://expressjs.com/4x/api.html。
瀏覽器會解析HTTP頭部里的cookie,根據過期時間決定保存策略。當再次訪問服務器時,瀏覽器會把cookie帶給服務器。服務器使用cookieParser解析后保存在Request對象的cookies屬性里,req.cookies本身是一個對象,解析出來的cookie,會被關聯到req.cookies的以cookie名字命名的屬性上。比如示例給cookie起的名字叫account,服務端解析出的cookie,就可以通過req.cookies.account來訪問。注意req.cookies.account本身既可能是簡單的值也可能是一個對象。在示例中通過res.cookie()發送的名為account的cookie,它的值是一個對象,在這種情況下,服務器這邊從HTTP請求中解析出的cookie也會被組裝成一個對象,所以我們通過req.cookies.account.account就可以拿到瀏覽器通過cookie發過來的用戶名。但如果瀏覽器沒有發送名為“account”的cookie,那req.cookies.account.hash這種訪問就會拋異常,所以我在代碼里使用req.cookies[“account”]這種方式來檢測是否有account這個cookie。
持久登錄
如果用戶每次訪問一個需要鑒權的頁面都要輸入用戶名和密碼來登錄,那就太麻煩了。所以,很多現代的網站都實現了持久登錄。我的示例使用cookie簡單實現了持久登錄。
在處理/login路徑上的POST請求時,如果登錄成功,就把用戶名、一個hash值、還有上次登錄時間保存在cookie里,并且設置cookie的有效期為60秒。這樣在60秒有效期內,瀏覽器后續的訪問就會帶cookie,服務端代碼從cookie里驗證用戶名和hash值,讓用戶保持登錄狀態。當過了60秒,瀏覽器就不再發送cookie,服務端就認為需要重新登錄,將用戶重定向到login頁面。
現在服務端的用戶信息就簡單的放在js代碼里了,非常丑陋,下次我們引入MongoDB,把用戶信息放在數據庫里。
關于怎么在Node.js中利用cookie保持登錄就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。