项目中凡是涉及到用户登录注册的都需要一个登录态来验证用户的登录状态,常用的登录台无外乎是token、session啊这些标识。这里我使用的是token字段。token一般会包含用户的个人信息,如:账号、账号id、用户名等等,更为安全的是加入一个自定义的盐(salt)一起加密,防止用户信息泄漏。下面就一起来使用一下:
说到token,肯定会想到后端是怎么知道前端给我的token是不是我传给他的有效值呢?就是说后端需要有个值去跟前端传过来的token进行比较才知道合法性。所以后端在生成token的同时自己也需要将这个生成的token存下来备用。我这里选用的redis。它是一个数据库,以键值对的形式存储,这样我就可以将生成的token存储下来了。至于redis的安装和部署这里就不累赘了,这里直接将使用。
在项目根目录下新建一个redis文件夹。其下新建一个redis.js文件。
//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就可以使用了。
本文使用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文件用来存放自定义的信息
⚠️ 这里建议盐最好乱写 写得越乱越好,各种字符都加上,并乱序写。
加密
我们在用户登录成功的时候将用户信息加密。所以我在我的管理员登录接口那写。
我的在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: '[email protected]',
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(就是所谓的同一个账户在多地登录会挤掉其他人的登录状态)。
解密
加密完之后客户端请求必然需要带上登录态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登录态控制接口请求权限。
上一篇:编写接口路由
下一篇:token控制接口权限