首頁  >  文章  >  web前端  >  如何使用Node.js操作cookie保持登入

如何使用Node.js操作cookie保持登入

php中世界最好的语言
php中世界最好的语言原創
2018-05-30 11:40:542018瀏覽

這次帶給大家怎麼使用Node.js操作cookie保持登錄,使用Node.js操作cookie保持登入的注意事項有哪些,下面就是實戰案例,一起來看一下。

這次來做一個網站登入的小例子,後面會用到。這個範例會用到Cookie、HTML表單、POST資料體(body)解析。

第一個版本,我們的使用者資料就寫死在js檔案裡。第二個版本會引入MongoDB來保存使用者資料。

範例準備

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;
}
p.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來存取使用者名稱。

#

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,把用户信息放在数据库里。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

怎样使用JS让数组合并和对象合并

怎样操作Node.js使用对话框ngDialog

以上是如何使用Node.js操作cookie保持登入的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn