木灵鱼儿
阅读:2030
koa框架21 扩展session mysql
session 的一些问题
- 无法共享,因为所有网站不可能共用一台服务器,可能会有多个
- 不安全,如果服务器崩溃,数据也会丢失
- 性能低,普通的session文件都是存放本地,io非常慢(相对)
于是就有两中方式进行优化:
- 丢数据库mysql
- 丢redis中
mysql
koa作者提供了一个接口store,这个接口也是在session的配置对象里的,他是一个对象,里面有三个生命周期函数,我称之为生命周期函数。
他们会在session操作完后触发。
server.use(session({
maxAge: 20 * 60 * 1000,
renew: true,
store: {
get(key, maxAge) { //获取
},
set(key, session, maxAge) { //设置
},
destroy(key) { //删除
}
}
}, server));
get是获取,session要从mysql获取的话,通过这个函数返回给koa数据。所有需要一个key,一个过期时间做判断
set是设置,设置的话,key是必须的,然后就是session对象本身的值了,还有就是过期时间
destroy是删除,删除的话有key就行了。
数据库创建
我们需要创建一个数据库session,数据库里面要有一个表,用于存储session信息的,命名为user_session
表里面有几个字段:
- ID 唯一,主键,int类型(int只有11位),不能为null,自增
- sessID,唯一,主键,varchar类型(80长度),不能为null,存放key
- view int类型,这个是我们自制的属性,用于存放浏览次数
- expires int类型,用于存放过期时间,必须为秒,因为int只有11位
四个字段创建好后,我们就可以连接这个数据了
const mysql = require("promise-mysql");
(async () => {
const sessionDB = await mysql.createPool({
host: "localhost",
user: "root",
pass: "123456",
database: "session"
});
})();
由于使用了promise-mysql,他的方法是一个异步的,所以做一个自运行函数,async开头
然后我们就可以先写get方法:
async get(key, maxAge) { //获取
const data = await sessionDB.query("SELECT* FROM user_session WHERE sessID=?", [key]);
if (data.length) { //存在
if (data.expires * 1000 < Date.now() + maxAge) { //过期
return {};
} else { //没过期
return {
view: data[0]["view"]
};
}
} else { //不存在
return {};
}
},
get是获取,从mysql里面拿数据,于是使用了SELETE 搜索所有user_session表里面,sessID要等于key的数据。
他会返回一个数组,所有的数据库操作都会返回一个数组,我们判断这个数组的length,判断是否有值。
没有就表示这是一个新用户,我们返回一个空对象,有的话,就判断时间有没有过期。
mysql里面存储的时间必须是秒,而且必须是正确的年月日那种秒,你不可能存个20秒,谁知道你这个20秒是哪段时间的,所以数据库的时间必须是当前时间+过期时间
所以我们乘以1000转换成秒,然后判断和当前时间的大小,如果小于,那就过期了,那就返回一个空对象。
如果没有过期,那就返回一个session里面存储的数据对象,像id,哪些肯定是不返回的,因为我们自制的只有一个view。
set方法:
async set(key, session, maxAge) { //设置
const isKey = await sessionDB.query("SELECT* FROM user_session WHERE sessID=?", [key]);
const expires = Math.floor((Date.now() + maxAge) / 1000);
if (isKey.length) { //已存在,进行更新数据
await sessionDB.query("UPDATE user_session SET view=?,expires=? WHERE sessID=?", [session.view, expires, key]);
} else { //不存在,直接添加
await sessionDB.query("INSERT INTO user_session (sessID,view,expores) VALUES(?,?,?)", [key, session.view, expires]);
}
},
设置的话需要判断下有没有存在这个key,如果存在是更新,不存在是直接添加。
由于数据库的时间是秒,所我们用当前时间+过期时间,除以1000转成秒,然后去除浮点数,得到存入数据的时间。
然后就是设置数据库的内容了。
删除
async destroy(key) { //删除
await sessionDB.query("DELETE FROM user_session WHERE sessID=?", [key]);
}
定时清理session
session是一个和浏览器建立连接后的一个临时存储,如果用户超时未连接的话,session就会失效了。
但是由于我们使用的是mysql存储,所有实际上弃用的session还存储在我们的mysql里面,我们需要及时清理掉
//定时清理 10分钟
setInterval(async () => {
await sessionDB.query("DELETE FROM user_session WHERE expires<?", [Math.floor(Date.now() / 1000)])
}, 10 * 60 * 1000)
创建一个无限定时器吗,十分钟运行一次,然后判断表里面时间小于当前时间的,直接删除。
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。
相关推荐
centos 安装最新版 node.js
[hide]先用yum 安装一次node先升级yum update 提示下载程序,回复y安装nodeyum install nodejsyum安装的node一般都不是最新的,所以我们还需要升级node的版本npm i n -g全局安装一个n插件n lts表示安装最新的稳定版!一些常用命令n ls查看已安装的node版本n rm 16.0.3删除16.0.3版本更多功能阅读插件文档:n等待安装完毕后,此时其实版本已经安装好了,但是输入node -v还是低版本,是因为centos的用户缓存问题。我们退出登录再登录就会刷新缓存了,此时的node -v输出的就是最新版本exit 退...
node npm yarn 如何同时运行多个指令
node的bash命令其实是有对应的指令字符的,但是,这个指令只能再linux,mac上才有生效,再windows是无效的,所以了解一下就行。命令说明&&顺序执行多条命令,当碰到执行出错的命令后将不执行后面的命令&并行执行多条命令||顺序执行多条命令,当碰到执行正确的命令后将不执行后面的命令|管道符windows上进行多命令"scripts": { "dev": "cross-env NODE_ENV=development webpack --env development --progress -...
分享一个利用vuex+session缓存侧边栏数据的方案
前言侧边栏导航大多数情况可能会由后端返回导航内容,第一是方便修改,第二是可以做一些菜单鉴权操作,但是这也会产生一个问题:侧边栏api请求会非常频繁如何避免这个问题,我们就需要使用缓存方案,在web中,缓存有:local,session,cookie,还有一些更复杂的就不说了,用不到。local会永久存储,这样我们刷新页面,数据以缓存优先的话,就会无法及时更新了。passcookie,spa用cookie很少了,而且他有容量限制,直接pass。那么只有session了,session会在页面关闭后自动清理,这样下次重新打开页面数据依旧可以保持最新,并且新建页面,同域名,session也是可...
koa教程2 HttpBearerAuth 传递token
前端发送token到后端,一般有三种方式:链接中携带token,query参数post这种请求body中携带token头信息携带token但是事实上http已经为我们提供了传token的方式,是一个相对来说比较安全的方式,就是HttpBearerAuth 其实他也是一个头信息来着,只是相对来说多了一点操作,在apipost软件中,我们可以直接可视化设置如果是axios,则是需要添加一个头信息:axios({ method: 'get', url: url, responseType: 'blob', headers: { 'Authoriz...

koa教程2 微信openid登录
微信的openid,前端登录时,会发送一个code码到后端,后端接收到这个code,然后配合appid、appsecret参数,像wx发送一个get请求。wx的请求地址:GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code具体查看文档:微信官方文档-服务端可以看到这段get请求有四个参数:属性类型默认值必填说明appidstring 是小程序 appIdsecretstring 是...
koa教程2 权限分级控制
在之前的代码中,我们使用了一个scope属性来表示用户的权限,但是scope是一个数字,他的阅读性是非常差的,当其他人开发的时候,可能并不知道你这个数字是什么意思?所以,我们需要将它转义成文本。为了方便判断,我们在enum.js中增加一个用户等级的对象enum.js://通用判断是否存在该类型 function isThisType(val) { let flag = false; Object.keys(this).find(key => { if (this[key] === val) { return flag = true; } })...
koa教程2 token鉴权
之前我们在token中传入我们的自定义参数,现在我们要做一个token鉴权,很简单,如果他的token合法,就可以访问这个路由,如果不合法,就打回。这个也不能说是真正意义上的鉴权,因为他还没有判断scope值,所以只能说是简单判断。做这个判断我们就可以通过中间件的形式,于是我们在middlewares目录下创建一个auth.js文件auth.js:const parseBearerToken = require("@utils/paresBearerToken"); const jwt = require("jsonwebtoken"); cons...
koa教程2 用户登录
登录类型用户注册之后,自然是用户登录,登录的时候我们需要考虑到登录的方式,往往在正式的项目里面,登录的方式可能会有很多种:账号密码登录手机号登录小程序一键登录那么我们登录的时候就要进行判断,设置一个type的属性,用于在登录时判断用户是什么类型的登录,这个由前端那边设定参数。type:1为账号密码登录,2为手机号登录,3为小程序一键登录作为判断,我们可以写一个查询表(枚举),在项目根目录创建一个lib目录,在里面创建一个enum.js文件。enum.js://通用判断是否存在该类型 function isThisType(val) { let flag = false; Obje...
node 自定义路径别名
用过vue cli的话,对路径中的@应该不陌生,他表示根目录,但是在node的环境里面,我们编写模块并引入时,并没有这种方便的写法。在node环境中,我们可以通过process.cwd()来表示根目录,但是这样的话,每次使用绝对路径引入,路径总是需要写一大堆拼接。const db = require(`${process.cwd()}/config/config`).database;这样略显麻烦,于是我们可以在全局写一个引入插件的方法:global.myRequire = function (path) { return require(`${process.cwd()}/${pa...
koa教程2 密码加密
默认情况下,我们通过User.create({ email, password, nickname });模型传入的数据,是无加密的,也就是说,在数据库中密码是明文显示的,这样肯定是不行,我们需要使用bcrypt插件进行加密yarn add bcryptjs --dev打开models/user.jsuser.js:const bcrypt = require("bcryptjs"); const { sequelize } = require(`${process.cwd()}/core/db`); const { Sequelize, Model, DataTy...
