项目中凡是涉及到用户登陆注册的都须要一个登陆态来验证用户的登陆状态,经常使用的登陆台无外乎是token、session啊这些标识。这里我使用的是token字段。token通常会包含用户的我的信息,如:帐号、帐号id、用户名等等,更为安全的是加入一个自定义的盐(salt)一块儿加密,防止用户信息泄漏。下面就一块儿来使用一下:前端
说到token,确定会想到后端是怎么知道前端给个人token是否是我传给他的有效值呢?就是说后端须要有个值去跟前端传过来的token进行比较才知道合法性。因此后端在生成token的同时本身也须要将这个生成的token存下来备用。我这里选用的redis。它是一个数据库,以键值对的形式存储,这样我就能够将生成的token存储下来了。至于redis的安装和部署这里就不累赘了,这里直接将使用。
在项目根目录下新建一个redis文件夹。其下新建一个redis.js文件。
web
//redis.js const Redis = require('ioredis')//导入模块 const redis = { port: 6379, // Redis port host: '127.0.0.1', // Redis host prefix: '***', //存诸前缀 ttl: 60 * 60 * 24 * 7 * 1000, //过时时间 family: 4, db: 0 } const redisClient = new Redis(redis) //导出备用 module.exports = redisClient
这样redis就可使用了。redis
本文使用jsonwebtoken进行加密和解密。
由于加密和解密是不少接口都须要用的东西,因此我将这些方法写到公共的部分去。
utils文件下建一个common.js,用来存放公共的方法。
数据库
//common.js const secret = require('./secret')//导入自定义的盐 const jwt = require('jsonwebtoken')//导入jsonwebtoken const verify = util.promisify(jwt.verify) // token解密 const common = { //定义一个对象 //加密 //后端生成惟一的key /* * paylod:包含来用户的信息 * secret.secret 自定义的盐(salt) * expiresIn 设置这个token的有效期 */ //jwt.sign是jsonwebtoken模块的一个方法,能够将传入的信息加密 getToken(paylod, expiresIn) { return jwt.sign(paylod, secret.secret, expiresIn) }, //解密 //根据收到的token获取用户信息 getUserInfo(token) { return verify(token, secret.secret) }, } //导出这个对象给外部使用 module.exports=common
新建一个secret.js文件用来存放自定义的信息npm
⚠️ 这里建议盐最好乱写 写得越乱越好,各类字符都加上,并乱序写。
json
加密后端
咱们在用户登陆成功的时候将用户信息加密。因此我在个人管理员登陆接口那写。api
个人在routes文件下的admin.js文件安全
//admin.js const router = require('koa-router')() const api = require('../controllers/api') const redisClient = require('../redis/redis.js') const common = require('../util/comon') router.prefix('/admin') //管理员登陆 router.post('/userLogin', async (ctx, next) => { /*写你的接口逻辑*/ //定义一个用户信息对象 const paylod = { name: '登陆用户的用户名', userid: '登陆用户的id',//登陆时可查表查拿到用户id author: '13414851033@163.com', type:'***', timestamp: new Date()//加个时间戳保证加密后token的惟一性 } /*核心代码*/ // 调用上面公共的token加密方法(注:这里是没有传盐进去的,我是直接在common文件引入来盐) // expiresIn设置token的有效期是7天 const token = await common.getToken(paylod, { expiresIn: '7 days' }) //每次登陆以前先清除掉全部的有关此用户的key(根据用户id) let preToken = await redisClient.get(result.userid) //这个preToken就是当初登陆时redis存下来的key await redisClient.del(preToken) //用token做为key、自定义的token前缀+token做为值 传key给前端做为校验 await redisClient.set(token, secret.identif + token) //再生成一对键值对 用来记录是属于哪一个用户的token 用户id做为key 传给前端的token(上一条键值对的key)做为值 await redisClient.set(result.userid,token) ctx.body = { status: 200, code: 200, message: '登陆成功', data: result, token: token//将token传给前端 } }
这样登陆成功后的话前端就能收到后端生成的惟一性的token了,同时我也生成了两对的键值对。一对是以token为key,以自定义的token前缀为value;一对是以用户id为key,以token做为值的数据。在用户登陆时拿到用户的id,在redis中清除掉以这个用户id为key的记录,再存入一条以token为key的记录。这样就能保证每次用户登陆该用户都是只有一个合法的key(就是所谓的同一个帐户在多地登陆会挤掉其余人的登陆状态)。session
解密
加密完以后客户端请求必然须要带上登陆态token来操做数据,可是不可能在客户端去传用户的数据,那样太不安全了,这样我上面生成token时将用户信息加进去的数据就有用处了,只要解密我就能知道这个token所携带的用户信息了。这个token客户端看到也是不知道用户信息的,因此相对来讲比较安全些。
在common.js写了一个获取前端传入的token(走请求头传入,不以参数的形式)
//common.js //根据请求头的信息获取前端传入的token getHeaderToken(ctx) { if (ctx.header && ctx.header.token) { return ctx.header.token } }
const common = require('../util/comon') //删除管理员 router.post('/****', async (ctx, next) => { let token = await common.getHeaderToken(ctx) let userInfo = await common.getUserInfo(token) //用户名 console.log(userInfo.name) //用户id console.log(userInfo.userid) }
因此,只要前端传入token,后端就能知道这个token所携带的用户的信息,方便后端处理数据所需的必备条件。
以上就是本此介绍token的使用,下文将介绍根据登陆台控制接口请求权限。
上一篇:编写接口路由
下一篇:token控制接口权限