原创转载请注明出处:http://agilestyle.iteye.com/blog/2355955
cookie 和 session
HTTP 是一个无状态协议,所以客户端每次发出请求时,下一次请求无法得知上一次请求所包含的状态数据,如何能把一个用户的状态数据关联起来呢?
比如在天猫的某个页面中,你进行了登陆操作。当你跳转到商品页时,服务端如何知道你是已经登陆的状态?
cookie
Express Cookie
https://www.npmjs.com/package/cookie-parser
var express = require('express'); var cookieParser = require('cookie-parser'); var app = express(); app.listen(3000); app.use(cookieParser()); app.get('/', function(req, res) { if (req.cookies.isVisit) { console.log(req.cookies); res.send("Welcome Again"); } else { res.cookie('isVisit', 1, { maxAge: 60 * 1000 }); res.send("Welcome First"); } });
Session
Express Session
https://www.npmjs.com/package/express-session
var express = require('express'); var session = require('express-session'); var app = express(); app.listen(3000); app.use(session({ secret: 'recommend to use 128 bytes random string', cookie: { maxAge: 60 * 1000 } })); app.get('/', function(req, res) { if (req.session.isVisit) { req.session.isVisit++; res.send('you viewed this page ' + req.session.isVisit + ' times'); } else { req.session.isVisit = 1; res.send("welcome first"); console.log(req.session); } });
Demo
使用cookie-parser和express-session实现一个用户持久登录的Demo
Project Directory
src
package.json
{ "name": "node-cookie-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "body-parser": "^1.16.0", "cookie-parser": "^1.4.3", "crypto-js": "^3.1.9-1", "ejs": "^2.5.5", "express": "^4.14.1", "express-session": "^1.15.0", "install": "^0.8.7", "npm": "^4.1.2", "parseurl": "^1.3.1", "path": "^0.12.7" } }
index.js
var express = require('express'); var ejs = require('ejs'); var path = require('path'); var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var routes = require('./routes/routes'); var app = express(); app.engine('.html', ejs.__express); app.set("port", process.env.PORT || 3000); app.set("views", path.join(__dirname, "views")); app.set("view engine", "html"); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use(cookieParser()); app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: true, cookie: { maxAge: 60 * 1000 } })); app.use(routes); app.listen(app.get('port'), function() { console.log('Server started on port ' + app.get('port')); });
Note:
将ejs模板后缀.ejs改为.html
使用body-parser、cookie-parser和express-session
routes.js
var express = require('express'); var CryptoJS = require("crypto-js"); var parseurl = require('parseurl'); var router = express.Router(); // mock-up user info start var userDB = [{ username: 'admin', password: md5('123456'), lastLogin: '' }, { username: 'node', password: md5('123456'), lastLogin: '' }, { username: 'express', password: md5('123456'), lastLogin: '' }]; // mock-up user info end function md5(password) { return CryptoJS.MD5(password).toString(); } function getLastLoginTime(username) { for (var i in userDB) { if (username === userDB[i].username) { return userDB[i].lastLogin; } } return ""; } function updateLastLoginTime(username) { for (var i in userDB) { if (username === userDB[i].username) { userDB[i].lastLogin = Date().toString(); } } } function authenticateUserInfoFromDB(username, password) { for (var i in userDB) { if (username === userDB[i].username) { if (password === userDB[i].password) { return 1; } else { return 0; } } } return -1; } function isAuthenticated(req, res) { if (typeof req.cookies.account === 'undefined' || req.cookies.account === null) { return false; } if (req.cookies.account !== null && req.cookies.account !== '') { var account = req.cookies.account; if (authenticateUserInfoFromDB(account.username, account.password) === 1) { console.log(req.cookies.account.username + " logged in."); return true; } } return false; } function ensureAuthenticated(req, res, next) { req.session.url = parseurl(req).pathname; console.log(req.session.url); if(isAuthenticated(req, res)) { next(); } else { res.redirect('/login?' + Date.now()); } } // router.use(function (req, res, next) { // res.locals.msg = req.msg; // next(); // }); router.get('/', function(req, res) { res.render('login'); }); router.get('/login', function(req, res) { if (isAuthenticated(req, res)) { res.redirect('/welcome?' + Date.now()); } else { res.render('login'); } }); router.post('/login', function(req, res, next) { var username = req.body.username; var password = md5(req.body.password); var url = req.session.url; console.log('in login url ' + url); var result = authenticateUserInfoFromDB(username, password); if (result === 1) { var lastLogin = getLastLoginTime(username); updateLastLoginTime(username); res.cookie('account', { username: username, password: password, lastLogin: lastLogin }, { maxAge: 60 * 1000 }); console.log('in login ' + req.session.url); if (typeof url !== 'undefined' && url !== null && url !== '') { res.redirect(url + '?' + Date.now()); } else { res.redirect('/welcome?' + Date.now()); } } else if (result === 0) { res.render('login', { msg: 'password incorrect' }); } else if (result === -1) { res.render('login', { msg: 'username not exist' }); } }); router.get('/logout', function(req, res, next) { res.clearCookie('account'); req.session.url = ''; res.redirect('/login?' + Date.now()); }); router.get('/welcome', ensureAuthenticated, function(req, res, next) { res.render('welcome', { msg: 'username: ' + req.cookies.account.username, title: 'Login Success', lastLogin: 'lastLogin: ' + (req.cookies.account.lastLogin === '' ? 'N/A' : req.cookies.account.lastLogin) }); }); router.get('/profile', ensureAuthenticated, function(req, res, next) { res.render('profile'); }); router.get('/messages', ensureAuthenticated, function(req, res, next) { res.render('messages'); }); module.exports = router;
Note:
这里仅仅是为了Demo,所以不操作数据库,仅仅mockup一下
使用md5加密password、获取最后登录时间、更新最后登录时间
验证输入的用户名和密码是否有效,1:有效 0:密码错 -1:用户不存在
这里采用将username和password存如一个名为account的cookie,将未登录前的输入的url存入session用于页面跳转
login的get和post路由表,其中post请求中将username和password存入名为account的cookie,时间为1分钟
welcome、profile、messages的get路由表,其中ensureAuthenticated起到了一个拦截器的作用
_footer.html
</body> </html>
_header.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"> <script type="text/javascript" src="//code.jquery.com/jquery-3.1.1.min.js"></script> <script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> </head> <body>
login.html
<% include _header.html %> <div class="container"> <div class="row"> <div class="col-sm-8 col-sm-offset-2"> <div class="page-header"> <div class="alert alert-info" role="alert"> <h4>This demo shows how to use cookie-parser and express-session</h4> <ul> <li> <a href="https://www.npmjs.com/package/cookie-parser" class="alert-link">cookie-parser</a> </li> <li> <a href="https://www.npmjs.com/package/express-session" class="alert-link">express-session</a> </li> </ul> </div> <% if(locals.msg) {%> <div class="alert alert-warning" role="alert"> <p><%= locals.msg %></p> </div> <% } %> </div> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">Log in</h3> </div> <div class="panel-body"> <form action="/login" method="post" class="form-horizontal"> <div class="form-group"> <label class="col-sm-4 control-label" for="username">Username</label> <div class="col-sm-5"> <input type="text" class="form-control" id="username" name="username" placeholder="Username" required="required" autofocus="autofocus"> </div> </div> <div class="form-group"> <label class="col-sm-4 control-label" for="password">Password</label> <div class="col-sm-5"> <input type="password" class="form-control" id="password" name="password" placeholder="Password" required="required"> </div> </div> <div class="form-group"> <div class="col-sm-5 col-sm-offset-4"> <input type="submit" value="Log in" class="btn btn-primary"> </div> </div> </form> </div> </div> </div> </div> </div> <% include _footer.html %>
Note:
如果不想在html页面中使用locals.msg
可以在routes.js中定义如下方法,之后在页面上可以直接使用 msg 即可取到值,但是这仅限于login.html页面,此msg是req.locals.msg, 与余下几个页面的 msg 无关
router.use(function (req, res, next) { res.locals.msg = req.msg; next(); });
messages.html
<% include _header.html %> <div class="container"> <div class="row"> <div class="col-sm-8 col-sm-offset-2"> <div class="page-header"> <div class="alert" role="alert"> <ul class="nav nav-pills"> <li role="presentation" class="active"><a href="/welcome">Welcome</a></li> <li role="presentation"><a href="/profile">Profile</a></li> <li role="presentation"><a href="/messages">Messages</a></li> <li role="presentation"><a href="/logout">Logout</a></li> </ul> </div> <div class="alert alert-success" role="alert"> <h2>In Messages Page</h2> </div> </div> </div> </div> </div> <% include _footer.html %>
profile.html
<% include _header.html %> <div class="container"> <div class="row"> <div class="col-sm-8 col-sm-offset-2"> <div class="page-header"> <div class="alert" role="alert"> <ul class="nav nav-pills"> <li role="presentation" class="active"><a href="/welcome">Welcome</a></li> <li role="presentation"><a href="/profile">Profile</a></li> <li role="presentation"><a href="/messages">Messages</a></li> <li role="presentation"><a href="/logout">Logout</a></li> </ul> </div> <div class="alert alert-success" role="alert"> <h2>In Profle Page</h2> </div> </div> </div> </div> </div> <% include _footer.html %>
welcome.html
<% include _header.html %> <div class="container"> <div class="row"> <div class="col-sm-8 col-sm-offset-2"> <div class="page-header"> <div class="alert" role="alert"> <ul class="nav nav-pills"> <li role="presentation" class="active"><a href="/welcome">Welcome</a></li> <li role="presentation"><a href="/profile">Profile</a></li> <li role="presentation"><a href="/messages">Messages</a></li> <li role="presentation"><a href="/logout">Logout</a></li> </ul> </div> <div class="alert alert-success" role="alert"> <h2><%= title %></h2> <h4><%= msg %></h4> <h4><%= lastLogin %></h4> </div> </div> </div> </div> </div> <% include _footer.html %>
Test
http://localhost:3000/
password incorrect
login success
Note:
默认登录成功话会直接跳转welcome页面
查看welcome的cookie信息
等1分钟后,在点击Profile Tab,会提示重新登录,重新输入用户名和密码之后,会直接跳转到Profle页面
Message页面
Reference
http://expressjs.com/en/4x/api.html
https://www.npmjs.com/package/cookie-parser
https://www.npmjs.com/package/express-session
https://github.com/alsotang/node-lessons/tree/master/lesson16
相关推荐
Express-Session的核心功能在于为每个用户创建一个唯一的session ID,这个ID存储在客户端的cookie中,而相应的session数据则存储在服务器端。这种设计既保证了安全性,又避免了大量数据存储在客户端。当用户发起新的...
本文将详细介绍在Express框架中如何使用session和cookie。 首先,cookie是由服务器发送到客户端(浏览器)的一小块数据,存储在用户的本地设备上。服务器可以通过检查客户端发送回的cookie来识别用户身份。在...
例如,`express-session`库提供了简单易用的Session管理接口,而`cookie-parser`库则可以帮助解析和操作Cookie。 1. **安装库**:首先,需要通过npm安装这两个库: ``` npm install express-session cookie-...
cookie-session不需要服务器端的任何数据库/资源,尽管会话的总数据不能超过浏览器的最大cookie大小。 cookie-session可以简化某些负载平衡方案。 cookie-session可用于存储“轻量”会话,并包含一个标识符以...
【标题】"前端开源库-express-mysql-session"是一个专为Express.js框架设计的会话管理中间件,它允许开发者将用户的会话数据存储在MySQL数据库中,而非传统的内存或文件系统。Express.js是Node.js平台上广泛使用的...
首先,`express-session`的基本功能是管理会话,通过在客户端的cookie中存储一个唯一的Session ID来实现。这样,每次客户端请求时,服务器可以通过这个ID查找并恢复相应的会话数据,这些数据实际上是存储在服务器端...
nestjs-cookie-session NestJS的惯用Cookie会话模块。 建立在之上 :smiling_face_with_sunglasses: 例 注册模块: // app.module.ts import { Module } from '@nestjs/common' ; import { ...
例如,使用`body-parser`解析请求体,`cookie-parser`处理cookie,`session`管理用户会话。定义路由处理GET、POST等请求,返回EJS模板或JSON数据。 4. **EJS**: 在EJS模板中,你可以用`<% %>`包裹JavaScript代码,`...
`express-session` 是一个中间件,用于处理和存储session数据,而`cookie-parser` 则用于解析客户端发送的cookie信息。通过以下命令可以在项目中安装它们: ```bash npm install express-session --save-dev npm ...
cookie-session, 基于简单cookie的会话中间件 cookie会话 基于简单cookie的会话中间件。用户会话可以通过 Cookies 以两种主要方式存储: 在服务器上或者在客户端上。 这个模块在cookie中存储会话数据,而像 express...
express-socket.io-session, socket.io 共享基于cookie的会话中间件 express-socket.io-sessionsocket.io 共享基于cookie的会话中间件。 使用英镑表示的> 4.0.0 和英镑> 1.0.0,不支持向后兼容。帮助我注意错误或者...
这与express-session有所不同,在express-session中,cookie包含一个会话ID,然后将其用于在服务器端映射数据。 默认情况下,Cookie具有 :alarm_clock: 有效期为7天,可通过[ expires ]设置,以days number单位。 ...
在这个主题中,我们将深入探讨如何使用Node.js的Express框架以及cookie来实现session身份认证。 首先,让我们理解一下基本概念: 1. **Session**: 一个session是服务器存储的关于用户的一段数据,通常包括用户ID等...
实现请求身份验证的方式很多,其中一种广泛接受的方式是使用服务器端产生的Session ID结合浏览器的Cookie实现对Session的管理,一般来说包括以下4个步骤: 1.服务器端的产生Session ID 2.服务器端和客户端存储...
本文将详细探讨如何解决Express中ajax跨域访问时session失效的问题。 首先,我们需要理解为何在跨域请求中session会失效。在HTTP协议中,由于同源策略的限制,浏览器不会在跨域请求中发送cookies,而session通常是...