项目要用到登录注册,就需要使用到 Cookie 和 Session 来保持登录状态,于是就简单研究了一下
Cookie 和 Session 的工作原理
前面已经专门发过一篇帖子记录 Cookie 和 Session 的工作原理了,不明白的小伙伴可以看看 Cookie、Session 是如何保持登录状态的?。
使用 Koa 的 Session 中间件
Koa 是一个简洁的框架,把许多小功能都拆分成了中间件,用一个洋葱模型保证了中间件丰富的可拓展性,我们要使用 Session 来保持登录状态,就需要引用 Session 中间件。
安装 Koa-Session 中间件
npm install koa-session --save
如果需要使用 TypeScript 进行开发,则需要引入对应的 TS 类型声明
npm install @types/koa-session --save
配置 Session
Koa-Session 需要做一些配置:
const session_signed_key = ["some secret hurr"]; // 这个是配合 signed 属性的签名 key
const session_config = {key: 'koa:sess', /** cookie 的 key。(默认是 koa:sess) */
maxAge: 4000, /** session 过期时间,以毫秒 ms 为单位计算。*/
autoCommit: true, /** 自动提交到响应头。(默认是 true) */
overwrite: true, /** 是否允许重写。(默认是 true) */
httpOnly: true, /** 是否设置 HttpOnly,如果在 Cookie 中设置了 "HttpOnly" 属性,那么通过程序 (JS 脚本、Applet 等) 将无法读取到 Cookie 信息,这样能有效的防止 XSS 攻击。(默认 true) */
signed: true, /** 是否签名。(默认是 true) */
rolling: true, /** 是否每次响应时刷新 Session 的有效期。(默认是 false) */
renew: false, /** 是否在 Session 快过期时刷新 Session 的有效期。(默认是 false) */
};
我们需要关注这几个配置:
- renew rolling
这两个都可以在用户访问的过程中刷新有效期,不至于让用户访问过程中 Session 过期成为未登录状态
- signed
这个是对客户端 Cookie 的签名,也就是用一个特点的字符加密,保证客户端 Cookie 不会被伪造出来
- httpOnly
打开这个使得通过程序 (JS 脚本、Applet 等) 无法读取 Cookie,大大提高了安全性
- maxAge
以 ms 为单位的过期时间
简单的使用
首先理一下思路
- 判断访问者的 Session 有没有过登录记录属性
- 如果有且值为 true,则为已登录,否则为未登录
- 如果为已登录,则不执行判断,直接返回已登录,如果为未登录,则执行下一步登录验证
- 如果验证成功,则返回的登录成功,并且在它的 session 中记下登录属性为 true,如果验证失败,则返回登录失败。
为了测试方便,以下用 Get 请求和一个固定的账号密码代替数据库查询,实际开发应该使用 POST 和数据库比对。同时为了测试方便,将过期时间设置为 4000ms,便于快速看到 Cookies 过期,实际开发应该设置长一些,比如几小时甚至几天,取决于业务需求。
const Koa = require('koa'); // 导入 Koa
const Koa_Session = require('koa-session'); // 导入 koa-session
// 配置
const session_signed_key = ["some secret hurr"]; // 这个是配合 signed 属性的签名 key
const session_config = {key: 'koa:sess', /** cookie 的 key。(默认是 koa:sess) */
maxAge: 4000, /** session 过期时间,以毫秒 ms 为单位计算。*/
autoCommit: true, /** 自动提交到响应头。(默认是 true) */
overwrite: true, /** 是否允许重写。(默认是 true) */
httpOnly: true, /** 是否设置 HttpOnly,如果在 Cookie 中设置了 "HttpOnly" 属性,那么通过程序 (JS 脚本、Applet 等) 将无法读取到 Cookie 信息,这样能有效的防止 XSS 攻击。(默认 true) */
signed: true, /** 是否签名。(默认是 true) */
rolling: true, /** 是否每次响应时刷新 Session 的有效期。(默认是 false) */
renew: false, /** 是否在 Session 快过期时刷新 Session 的有效期。(默认是 false) */
};
// 实例化
const app = new Koa();
const session = Koa_Session(session_config, app)
app.keys = session_signed_key;
// 使用中间件,注意有先后顺序
app.use(session);
app.use(ctx => {
const databaseUserName = "testSession";
const databaseUserPasswd = "noDatabaseTest";
// 对 /favicon.ico 网站图标请求忽略
if (ctx.path === '/favicon.ico') return;
if (!ctx.session.logged) { // 如果登录属性为 undefined 或者 false,对应未登录和登录失败
// 设置登录属性为 false
ctx.session.logged = false;
// 取请求 url 解析后的参数对象,方便比对
// 如?nickname=post 修改 &passwd=123 解析为{nickname:"post 修改",passwd:"123"}
let query = ctx.request.query;
// 判断用户名密码是否为空
if (query.nickname && query.passwd) {
// 比对并分情况返回结果
if (databaseUserName == query.nickname) { // 如果存在该用户名
// 进行密码比对并返回结果
ctx.body = (databaseUserPasswd == query.passwd) ? "登录成功" : "用户名或密码错误";
ctx.session.logged = true;
} else { // 如果不存在该用户名 // 如果用户名不存在
ctx.body = "用户名不存在";
}
} else {ctx.body = "用户名密码不能为空";}
} else {ctx.body = "已登录";}
}
);
app.listen(3000);
console.log("Koa 运行在:http://127.0.0.1:3000");
运行一下,控制台输出:
Koa 运行在:http://127.0.0.1:3000
访问 http://127.0.0.1:3000,可以看到我们没有填写登录参数,然后返回了用户名密码不能空,并且按下 F12,点击 Cookies 再点击 http://127.0.0.1:3000,看到了我们的 SessionId 被记录到了 Cookies 中,说明 Session 生效了。
我们静置一会但不刷新页面,再点击 Cookies 后重新点击,http://127.0.0.1:3000(刷新 Cookies 显示),发现我们的 SessionId 不见了,说明我们的过期时间也生效了
我将 Cookies 的数据截详细一点就是这样的,可以看到有个过期时间:
我们再访问 http://127.0.0.1:3000/?nickname=123 和 http://127.0.0.1:3000/?passwd=123,都输出了
访问 http://127.0.0.1:3000/?nickname=123&&passwd=123,输出
访问 http://127.0.0.1:3000/?nickname=testSession&&passwd=123,输出
最后尝试正确的用户名密码 http://127.0.0.1:3000/?nickname=testSession&&passwd=noDatabaseTest
,输出
尝试再次访问 http://127.0.0.1:3000/?nickname=testSession&&passwd=noDatabaseTest
,输出
在有效期限内访问别的页面 http://127.0.0.1:3000/,输出
有效期内不断刷新就能保持登录状态
有效期内没有重新操作页面刷新状态就会自然过期