亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Node.js使用cookie保持登錄的方法

 更新時(shí)間:2018年05月11日 08:55:45   作者:foruok  
本篇文章主要介紹了Node.js使用cookie保持登錄的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

這次來(lái)做一個(gè)網(wǎng)站登錄的小例子,后面會(huì)用到。這個(gè)示例會(huì)用到Cookie、HTML表單、POST數(shù)據(jù)體(body)解析。

第一個(gè)版本,我們的用戶(hù)數(shù)據(jù)就寫(xiě)死在js文件里。第二個(gè)版本會(huì)引入MongoDB來(lái)保存用戶(hù)數(shù)據(jù)。

示例準(zhǔn)備

1. 使用express創(chuàng)建應(yīng)用

就下面的命令序列:

express LoginDemo
cd LoginDemo
npm install

2. 登錄頁(yè)面

登錄頁(yè)面的jade模板為login.jade,內(nèi)容如下:

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') 賬號(hào):
      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編碼。

這個(gè)模板的最后是一條動(dòng)態(tài)消息,用于顯示登錄錯(cuò)誤信息,msg變量由應(yīng)用程序傳入。

我給login頁(yè)面寫(xiě)了個(gè)簡(jiǎn)單的CSS,login.css文件,內(nèi)容如下:

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;
}

請(qǐng)把login.css放在public/stylesheets目錄下。

3. profile頁(yè)面

登錄成功后會(huì)顯示配置頁(yè)面,profile.jade頁(yè)面內(nèi)容:

doctype html
html
 head
  meta(charset='UTF-8')
  title= title
 body
  p #{msg}
  p #{lastTime}
  p 
   a(href='/logout') 退出

profile.jade放在views目錄下。profile頁(yè)面顯示一條登錄成功的消息,還顯示上次登錄時(shí)間,最后提供了一個(gè)退出鏈接。

4. app.js改動(dòng)

我改動(dòng)了app.js,以便用戶(hù)在沒(méi)有登錄時(shí)訪(fǎng)問(wèn)網(wǎng)站自動(dòng)跳轉(zhuǎn)到login頁(yè)面。新的app.js內(nèi)容如下:

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,把認(rèn)證、登錄、登出等邏輯放在里面,首先要把users.js轉(zhuǎn)為UTF-8編碼(sorry,硬編碼了漢字哈)。內(nèi)容:

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:"密碼錯(cuò)誤"});
  break;
 case 2: //user not found
  console.log("user not found");
  res.render('login', {msg:"用戶(hù)名不存在"});
  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;

如你所見(jiàn),我內(nèi)置了兩個(gè)賬號(hào),admin和foruok,登錄時(shí)就驗(yàn)證這兩個(gè)賬號(hào),不對(duì)就報(bào)錯(cuò)。

好了,執(zhí)行“npm start”,然后在瀏覽器里打開(kāi)“http://localhost:3000”,可以看到下面的效果:

折騰幾次,登錄,退出,再次登錄,效果如下:

好啦,這就是這個(gè)示例的效果。接下來(lái)我們來(lái)解釋一下用到概念和部分代碼。

處理POST正文數(shù)據(jù)

我們?cè)谑纠惺褂昧薍TML表單來(lái)接收用戶(hù)名和密碼,當(dāng)input元素的類(lèi)型為submit時(shí),點(diǎn)擊它,瀏覽器會(huì)把表單內(nèi)的數(shù)據(jù)按一定的格式組織之后編碼進(jìn)body,POST到指定的服務(wù)器地址。用戶(hù)名和密碼,在服務(wù)器端,可以通過(guò)HTML元素的名字屬性的值找出來(lái)。

服務(wù)器解析表單數(shù)據(jù)這一過(guò)程,我們不用擔(dān)心,用了express的body-parser中間件,它會(huì)幫我們做這件事,只要做簡(jiǎn)單的配置即可。而且這些配置代碼,express generator都幫我們完成了,如下:

//加載body-parser模塊
var bodyParser = require('body-parser');
...
//應(yīng)用中間件
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

我們處理/login路徑上的POST請(qǐng)求的代碼在users.js里,從“router.post(‘/login'…”開(kāi)始(94行,要是markdown能自動(dòng)給代碼插入行號(hào)就好了)。引用登錄表單內(nèi)的用戶(hù)名的代碼如下:

var userName = req.body.login_username;

注意到了吧,express.Request對(duì)象req內(nèi)有解析好的body,我們使用login_username來(lái)訪(fǎng)問(wèn)用戶(hù)名。而login_username就是我們?cè)贖TML里的input元素的name屬性的值。就這么關(guān)聯(lián)的。password也類(lèi)似。

cookie

cookie,按我的理解,就是服務(wù)器發(fā)給瀏覽器的一張門(mén)票,要訪(fǎng)問(wèn)服務(wù)器內(nèi)容,可以憑票入場(chǎng),享受某種服務(wù)。服務(wù)器可以在門(mén)票上記錄一些信息,從技術(shù)角度講,想記啥記啥。當(dāng)瀏覽器訪(fǎng)問(wèn)服務(wù)器時(shí),HTTP頭部把cookie信息帶到服務(wù)器,服務(wù)器解析出來(lái),校驗(yàn)當(dāng)時(shí)記錄在cookie里的信息。

HTTP協(xié)議本身是無(wú)狀態(tài)的,而應(yīng)用服務(wù)器往往想保存一些狀態(tài),cookie應(yīng)運(yùn)而生,由服務(wù)器頒發(fā),通過(guò)HTTP頭部傳給瀏覽器,瀏覽器保存到本地。后續(xù)訪(fǎng)問(wèn)服務(wù)器時(shí)再通過(guò)HTTP頭部傳遞給服務(wù)器。這樣的交互,服務(wù)器就可以在cookie里記錄一些用戶(hù)相關(guān)的信息,比如是否登錄了,賬號(hào)了等等,然后就可以根據(jù)這些信息做一些動(dòng)作,比如我們示例中的持久登錄的實(shí)現(xiàn),就利用了cookie。還有一些電子商務(wù)網(wǎng)站,實(shí)現(xiàn)購(gòu)物車(chē)時(shí)也可能用到cookie。

cookie存儲(chǔ)的是一些key-value對(duì)。在express里,Request和Response都有cookie相關(guān)的方法。Request實(shí)例req的cookies屬性,保存了解析出的cookie,如果瀏覽器沒(méi)發(fā)送cookie,那這個(gè)cookies對(duì)象就是一個(gè)空對(duì)象。

express有個(gè)插件,cookie-parser,可以幫助我們解析cookie。express生成的app.js已經(jīng)自動(dòng)為我們配置好了。相關(guān)代碼:

var cookieParser = require('cookie-parser');
...
app.use(cookieParser());

express的Response對(duì)象有一個(gè)cookie方法,可以回寫(xiě)給瀏覽器一個(gè)cookie。

下面的代碼發(fā)送了一個(gè)名字叫做“account”的cookie,這個(gè)cookie的值是一個(gè)對(duì)象,對(duì)象內(nèi)有三個(gè)屬性。

復(fù)制代碼 代碼如下:
res.cookie("account", {account: userName, hash: hash, last: lastTime}, {maxAge: 60000});

res.cookie()方法原型如下:

res.cookie(name, value [, options])

文檔在這里:http://expressjs.com/4x/api.html

瀏覽器會(huì)解析HTTP頭部里的cookie,根據(jù)過(guò)期時(shí)間決定保存策略。當(dāng)再次訪(fǎng)問(wèn)服務(wù)器時(shí),瀏覽器會(huì)把cookie帶給服務(wù)器。服務(wù)器使用cookieParser解析后保存在Request對(duì)象的cookies屬性里,req.cookies本身是一個(gè)對(duì)象,解析出來(lái)的cookie,會(huì)被關(guān)聯(lián)到req.cookies的以cookie名字命名的屬性上。比如示例給cookie起的名字叫account,服務(wù)端解析出的cookie,就可以通過(guò)req.cookies.account來(lái)訪(fǎng)問(wèn)。注意req.cookies.account本身既可能是簡(jiǎn)單的值也可能是一個(gè)對(duì)象。在示例中通過(guò)res.cookie()發(fā)送的名為account的cookie,它的值是一個(gè)對(duì)象,在這種情況下,服務(wù)器這邊從HTTP請(qǐng)求中解析出的cookie也會(huì)被組裝成一個(gè)對(duì)象,所以我們通過(guò)req.cookies.account.account就可以拿到瀏覽器通過(guò)cookie發(fā)過(guò)來(lái)的用戶(hù)名。但如果瀏覽器沒(méi)有發(fā)送名為“account”的cookie,那req.cookies.account.hash這種訪(fǎng)問(wèn)就會(huì)拋異常,所以我在代碼里使用req.cookies[“account”]這種方式來(lái)檢測(cè)是否有account這個(gè)cookie。

持久登錄

如果用戶(hù)每次訪(fǎng)問(wèn)一個(gè)需要鑒權(quán)的頁(yè)面都要輸入用戶(hù)名和密碼來(lái)登錄,那就太麻煩了。所以,很多現(xiàn)代的網(wǎng)站都實(shí)現(xiàn)了持久登錄。我的示例使用cookie簡(jiǎn)單實(shí)現(xiàn)了持久登錄。

在處理/login路徑上的POST請(qǐng)求時(shí),如果登錄成功,就把用戶(hù)名、一個(gè)hash值、還有上次登錄時(shí)間保存在cookie里,并且設(shè)置cookie的有效期為60秒。這樣在60秒有效期內(nèi),瀏覽器后續(xù)的訪(fǎng)問(wèn)就會(huì)帶cookie,服務(wù)端代碼從cookie里驗(yàn)證用戶(hù)名和hash值,讓用戶(hù)保持登錄狀態(tài)。當(dāng)過(guò)了60秒,瀏覽器就不再發(fā)送cookie,服務(wù)端就認(rèn)為需要重新登錄,將用戶(hù)重定向到login頁(yè)面。

現(xiàn)在服務(wù)端的用戶(hù)信息就簡(jiǎn)單的放在js代碼里了,非常丑陋,下次我們引入MongoDB,把用戶(hù)信息放在數(shù)據(jù)庫(kù)里。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論