koa结合redis生成token
前言
之前我是将token挂载到上下文,这里做了笔记,
但是后来我发现如果服务重启,他就失效。
业界常用的做法是将token放入redis管理
生成令牌
如果有 token 效期受 redis 控制了,那还要jwt就不要再控制了
import jwt from 'jsonwebtoken';
import { redis } from '../middleware/redis';
// 生成唯一令牌
const genToken = async (payload) => {
const token = jwt.sign(payload, process.env.JWT_SECRET);
// 只在Redis中控制过期时间
await redis.set(`token:${token}`, JSON.stringify(payload), 'EX', 60 * 60);
return token;
};
export default genToken;
移除原先的校验
并且移除 app.ts 中 koa-jwt 的校验代码,即下面代码就要删除了
// 1. 使用 koa-jwt 中间件(验证 JWT)
const koaJwt = KoaJwt({
secret: process.env.JWT_SECRET,
}).unless({
path: [
/^\/api\/login$/,
/^(?!\/api).*/,
/^\/api\/send-code$/,
],
custom: (ctx) => {
// 放行带有 share 参数的 GET 请求
if (ctx.method === 'GET' && ctx.query.share==='true') {
return true;
}
return false;
}
});
app.use(koaJwt);
加入新的校验
import jwt from 'jsonwebtoken';
import { redis } from './redis';
import context from '../utils/reques-context';
const redisJwt = async (ctx, next) => {
// 跳过不需要验证的路径
const skipPaths = [/^\/api\/login$/, /^(?!\/api).*/, /^\/api\/send-code$/];
const shouldSkip = skipPaths.some((pattern) => pattern.test(ctx.path)) || (ctx.method === 'GET' && ctx.query.share === 'true');
if (!shouldSkip && ctx.header?.authorization) {
const token = ctx.header.authorization.replace('Bearer ', '');
try {
// 校验jwt token签名
const info = jwt.verify(token, process.env.JWT_SECRET);
console.log(info)
// 检查Redis中是否存在该token
const tokenData = await redis.get(`token:${token}`);
if (!tokenData) {
ctx.status = 401;
ctx.body = { msg: 'Token已失效' };
return;
}
ctx.state.userId = info.id;
context.set('userId', ctx.state.userId);
} catch (err) {
ctx.status = 401;
ctx.body = { msg: 'Token无效' };
return;
}
} else if (!shouldSkip) {
ctx.status = 401;
ctx.body = { msg: '缺少认证信息' };
return;
}
await next();
};
export default redisJwt;
使用
在入口中注册
// 1. 使用 redis-jwt 中间件(验证 JWT)
app.use(redisJwt);
在业务代码中使用
// 获取当前用户信息
router.get('/current', async (ctx) => {
console.log(ctx.state.userId);
const user = await queryOne({id:ctx.state.userId})
ctx.body = JsonResult.success(user);
});