node+koa+jsonwebtoken鉴权jwt实战
涉及到的库:
如果对jwt不熟悉的话可以去了解一下前置知识:node实现jwt
主要是依靠jsonwebtoken的两个api:
生成token: sign()
校验token: verify()
这是我封装后的jwtUtils:
import jwt from 'jsonwebtoken';
import { sha256 } from "./cryptoUtils";
import { Context } from "koa";
import logger from './logger';
import emitter from './emitter';
import { HttpStatus } from '../middlewares/responseHandler';
export interface JwtOptions {
secret: string;
expiresIn: number;
excludePath: Array<RegExp>;
}
export enum TokenErrorType {
TOKEN_EXPIRED_ERROR = 'TokenExpiredError',
JSON_WEB_TOKEN_ERROR = 'JsonWebTokenError'
}
class JwtUtils {
public secret: string;
public expiresIn: number;
public excludePath: Array<RegExp>;
private static instance: JwtUtils = null;
public constructor(options: JwtOptions) {
const { secret, expiresIn, excludePath } = options;
logger.info(options)
this.secret = sha256(secret);
this.excludePath = excludePath
this.expiresIn = expiresIn;
}
public async validateToken(ctx: Context, next): Promise<void> {
const path = ctx.request.path;
// 校验是否为请求白名单
const isExcludePath = this.excludePath.some(function (e: RegExp) {
return e.test(path);
})
if (isExcludePath) {
return await next();
}
const token = (ctx.get('Authorization') || '').split(' ').pop();
if (!token) {
emitter.emit('errorResponse', ctx, HttpStatus.UNAUTHORIZED, '请求未携带token,请检查请求头');
return;
}
try {
const { id } = jwt.verify(token, this.secret) as { id: number } // 校验不通过会抛出错误
} catch (error) {
const { name } = error;
switch (name) {
case TokenErrorType.TOKEN_EXPIRED_ERROR: {
emitter.emit('errorResponse', ctx, HttpStatus.UNAUTHORIZED, 'token已过期');
break;
}
case TokenErrorType.JSON_WEB_TOKEN_ERROR: {
emitter.emit('errorResponse', ctx, HttpStatus.UNAUTHORIZED, '非法token');
break;
}
default:
break;
}
return;
}
await next()
}
public auth(ctx: Context, payload) {
const token = jwt.sign(payload, this.secret, { expiresIn: this.expiresIn })
return token;
}
public static getInstance() {
if (!JwtUtils.instance) {
JwtUtils.instance = new JwtUtils({
excludePath: [
/\/user\/login/,
/\/swagger-html/,
/\/swagger-json/,
/\/uploads\/\w+/,
/\/captcha/,
],
secret: 'Suk5201314lover',
expiresIn: 60 * 60 * 24
});
}
return JwtUtils.instance;
}
}
const jwtUtils = Object.freeze(JwtUtils.getInstance());
export default jwtUtils;
首先生成token的api我们在用户授权也就是登录接口那里如果登录成功就要生成token和用户信息一起返回:
核心API说明:
sign方法传入的第一个参数就是用户载荷,也就是要加密的用户信息,可以传入用户id或者其他,第二个koa的上下文对象,第二个参数就是token加密的秘钥,这里博主用的sha256加密的秘钥,第三个options选项可以传入token过期时间,单位是秒
verify方法传入的第一个参数就是token,token来自用户请求的请求头信息,Bearer [token],把token截取出来就行了,第二个参数就是加密时的秘钥,这里传入用于解密token,返回值是sign方法传入的用户信息载荷,方法执行校验token如果失败就会抛出错误,就是利用这点来判断token是否有效
因为是通过koa中间件实现的,所以这里传入校验方法时是做为回调给koa去调用的,封装的this指向会错,所以手动指回来,这里是个细节划重点容易错
然后要注意的就是白名单要放行,不能拦截授权的接口,比如login,反正不用授权的都可以加入白名单
基本原理就是这样了,up主因为时间关系(懒)代码只贴了核心部分,主要是实现原理讲解,有不懂的地方可以留言或者私信up