背景
做了四年的前端开发, 对外一直说自己是 web 开发, 那么身为一个 Web 开发怎能不知道 session 与 cookie 以及其管理方式呢~
Login 涉及技术栈: Node.JS,MongoDB,Express 以及 html,CSS,JS
了解 session 与 cookie 之前首先要知道什么是 http 协议, 为什么会出现 session 与 cookie, 可以参考很久之前总结的(戳我: session 与 cookie).
http 协议:
http 即超文本传输协议(万维网定义的), 一种基于浏览器请求与服务器响应的链接, 它是一个很纯粹的传输协议. http 协议主要的特征就是它是一种无状态的协议(只针对 cookie 与 session 问题), 在客户端连续向服务器发送请求的时候, 每次请求的过程中只要数据交换完毕, 服务器与客户端就会断开连接, 再次请求的时候会重新连接客户端与服务器, 这样服务器记录上次的对话, 那么问题来了, 如何让服务器知道是哪个客户端向自己发出的请求呢, 这个时候 cookie 就诞生了~
什么是 cookie
cookie 是一小段文本信息, 这段小文本信息由服务器首次响应客户端时发送的, 在客户端向服务器首次发送请求的时候, 服务器会判断是否要记录客户端的身份, 如果需要, 此时就会在响应中 (response) 给客户端发送一个 cookie, 该 cookie 文本信息保存在 http 的报头里, 当浏览器会将 cookie 保存起来, 当该浏览器再次发送请求时会携带 cookie, 服务器检查 cookie 来识别浏览器请求, 这里 cookie 的特征就不在说明了. 下面我们上代码!
页面代码:
- <!doctype HTML>
- <HTML lang="en">
- <head>
- <meta charset="utf-8">
- <script type="text/javascript" src="jquery-3.3.1.min.js">
- </script>
- <script type="text/javascript" src='test.js'>
- </script>
- </head>
- <body>
- <section>
- <h3>
- Register
- </h3>
- <div>
- <label style="display:inline-block; width: 100px;" id="register-user-name-label"
- htmlfor="register-user-name-input">
- register:
- </label>
- <input style="display:inline-block; width: 200px;" id="register-user-name-input"
- type="text" />
- </div>
- <div>
- <label style="display:inline-block; width: 100px;" id="register-password-label"
- htmlfor="register-password-input">
- pasword:
- </label>
- <input style="display:inline-block; width: 200px;" id="register-password-input"
- type="text" />
- </div>
- <button id="register" type="button">
- Register
- </button>
- </section>
- <section>
- <h3>
- Login
- </h3>
- <div>
- <label style="display:inline-block; width: 100px;" id="user-name-label"
- htmlfor="user-name-input">
- login name:
- </label>
- <input style="display:inline-block; width: 200px;" id="user-name-input"
- type="text" />
- </div>
- <div>
- <label style="display:inline-block; width: 100px;" id="password-label"
- htmlfor="password-input">
- pasword:
- </label>
- <input style="display:inline-block; width: 200px;" id="password-input"
- type="text" />
- </div>
- <button id="login" type="button">
- Login
- </button>
- </section>
- <script type="text/javascript" src='test.js'>
- </script>
- </body>
- </HTML>
很简单, 一个注册按钮一个登陆按钮(ps: 代码冗余请忽视, 就是为了做个 demo 用).
首先注册一个 user:
注册 user 之后我们查看 db
然后我们用这个 user 进行登陆操作, 重点来啦~
首先刷新下页面, 调用获取 user 方法, 看下效果
代码如下:
- App.get('/userInfo', function (req, res) {
- //cookie
- if (req.cookies.userInfo) {
- console.log('login successfully')
- }
- else {
- console.log('session timeout.');
- }
- res.status(200).JSON(req.cookies.userInfo);
- //session
- // if (req.session.userInfo) console.log('login successfully');
- // else console.log('session timeout.');
- })
好了先 mark 下, 回头再来做对比, 下面执行 login 操作, 这里要上代码了.
首先引入一个中间件:
var cookie = require('cookie-parser');
使用它:
- App.use(cookie('express_cookie'));
- //cookie
- App.post('/login', function (req, res) {
- User.findOne({
- username: req.body.username
- }).then(function (userInfo) {
- if (!userInfo) {
- console.log('user is not exist.');
- return;
- }
- var data = {};
- data['username'] = userInfo.username;
- data['password'] = userInfo.password;
- res.cookie('username', JSON.stringify(data), { maxAge: 900000, httpOnly: true });
- res.status(200).JSON(data);
- })
- .catch(function (e) {
- console.log(e);
- })
- })
这里我们可以设置 cookie 的 httpOnly 属性, 最大生命周期, 等等, 然后我们先在 db 内查询当前登录 user, 如果已经注册过, 我们获取 user 信息并存入 cookie 中. 这时候看下前端的响应有什么不同.
可以看见, 服务器颁发的 cookie 在响应的 header 中的 Set-Cookie 中. 似不似发现不同了. 这时候我们在刷新下页面调用 userInfo 方法看下效果.
咦, 我们发现这次的请求里面居然有 cookie 了, 就这么神奇(Ps: 我们要相信科学!).
debug 下看看
服务器端有我们想要的 cookie 信息了. 这样服务器就可以根据 cookie 知道了我们每一次的请求是不是同一个人了.
总结: 首先 cookie 是服务器颁发的, 然后随着响应返回给客户端也就是我们的浏览器, 浏览器保存 cookie, 每一次发送请求都会带着这个 cookie 来让服务器知道, 嗯我就是上次的那个人, 到这里对 cookie 是不是多少了解了一些呢~
好了, 那么现在很多浏览器都是禁用 cookie 的, 原因是啥呢~, 由于 cookie 是可以被获取的以及 cookie 是可以修改的, 这时候引出了 Web 安全方面的姿势, 跨站脚本攻击以及跨站协议伪造, 可以参考我之前写的关于 XSS 攻击(戳我: 什么是 XSS 以及 CFRS), 那么如果 cookie 禁用了我们该怎么办呢? 这时候 session 就诞生了.
何为 session:
session 本省并不存在, 只是一个概念, session 是服务器用来记录客户端状态的机制, 不同于 cookie 保存在浏览器中, session 是保存在服务器上的, 服务器会根据 cookie 生成一个 session id 存在服务器上, 当请求再次抵达服务器时, 服务器发出响应时会将 session id 存在 cookie 内一同反回给浏览器, 这就是 session.session 具体哪些特点这里就不写啦, 话不多说, 上代码.
首先引入一个中间件:
var session = require('express-session');
使用它
- App.use(cookie('express_cookie'));
- App.use(session({
- secret: 'express_cookie',
- resave: false,
- saveUninitialized: true,
- cookie: { maxAge: 60 * 1000 * 30 },
- rolling: true,
- }));
- App.post('/login', function (req, res) {
- User.findOne({
- username: req.body.username
- }).then(function (userInfo) {
- if (!userInfo) {
- console.log('user is not exist.');
- return;
- }
- var data = {};
- data['username'] = userInfo.username;
- data['password'] = userInfo.password;
- req.session.userInfo = data;
- res.status(200).JSON(data);
- })
- .catch(function (e) {
- console.log(e);
- })
- })
现在我们登录一下看下效果:
会发现, 多了一个 Cookie, 而且 Cookie 里面多了一个 sid, 不用联想了, 这就是 sessionId, 这时候我们在刷新一下页面看下 userInfo 变成啥样了呢?
可以清晰的看到再次请求的时候, sessionId 会装在 Cookie 中, 然后发送给服务器, 这时候服务器就知道了, 咦, 原来是上个人. 这就是 session.
由于现在服务器 session 存入的方式我们采用了服务器自带的内存, 也叫 session memory. 如果 server 挂了怎么办呢, 挂掉了内存就释放了啊, session 就没了啊. 这个时候就引出了另外一个问题, session 的可持续化.
session 的可持续化
session 的可持续化方式简单的理解就是让 session 可以在生命周期内一直存在, 可以把 session 存入 db 中, 可以是 MongoDB, 可以是 Redis, 上代码, 我们这里用 MongoDB 吧, 个人比较喜爱.
引入中间件:
- var MongoStore = require('connect-mongo')(session);
- App.use(cookie('express_cookie'));
- App.use(session({
- secret: 'express_cookie',
- resave: false,
- saveUninitialized: true,
- cookie: { maxAge: 60 * 1000 * 30 },
- rolling: true,
- store: new MongoStore({
- url: 'mongodb://127.0.0.1:27017/demo',
- collection: 'sessions'
- })
- }));
- App.post('/login', function (req, res) {
- User.findOne({
- username: req.body.username
- }).then(function (userInfo) {
- if (!userInfo) {
- console.log('user is not exist.');
- return;
- }
- var data = {};
- data['username'] = userInfo.username;
- data['password'] = userInfo.password;
- req.session.userInfo = data;
- res.status(200).JSON(data);
- })
- .catch(function (e) {
- console.log(e);
- })
- })
http 请求与响应部分我们就不看了, 直接看 server 跟 DB.
server 中我们将 userInfo 放入 session 中
- var data = {
- };
- data['username'] = userInfo.username;
- data['password'] = userInfo.password;
- req.session.userInfo = data;
查看 DB
咦, 一条 session 就在 DB 中诞生了, 这里要注意的是, session 不是设置的时候就会存入 DB 中的, 包括内存等等, 而且响应成功的时候才会存入, 一定要注意, 不然坑的就是你.
然后刷新页面看下效果.
似不似, session 中就有了 user 信息. 好了到这里关于 session 持久化的问题也解决了.
登出功能就很简单了, 销毁 session 就 ok 了, 代码如下:
- App.get("/loginOut",function(req,res){
- req.session.destroy(function(err){
- console.log(err);
- })
- res.send('退出登录成功');
- });
Redis 方式:
中间件以及使用:
- var RedisStrore = require('connect-redis')(session);
- App.use(session({
- secret: 'express_cookie',
- resave: false,
- saveUninitialized: true,
- cookie: { maxAge: 60 * 1000 * 30 },
- rolling: true,
- store: new RedisStrore({})
- }));
总结: 第一次登陆请求的时候, 服务器会颁发一个 sessionId, 响应的时候将 sessionId 放入 cookie 中返回给浏览器, 此时 session 已存入 DB 中, 当再次请求的时候携带着 sessionId 进入服务器中, 获取 session 信息, 服务器还是会记得我.
时间不早了. 在这块的知识还涉及什么时候 token,token 认证方式, 以及什么是 jwt. 以后有时间会继续更新的.
代码地址: https://github.com/Dqhan/Login
大半夜的, 写个博客不容易, 请博客园管理员高抬贵手, 让我留在首页吧.
来源: https://www.cnblogs.com/moran1992/p/10793748.html