egg-jwt是适配egg的jwt鉴权插件,在讲这个插件如何使用前,先回顾下jwt的原理。

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

JWT请求流程

  1. 浏览器向服务器请求jwt口令
  2. 服务器创建jwt,并返回给浏览器
  3. 浏览器接受到服务器返回的jwt,并保存在本地(cookie、localStorage、sessionStorage均可)
  4. 浏览器之后向服务器发送的所有请求,默认都带上jwt,供服务器校验
  5. 服务器接收到请求,校验附带的jwt口令,校验通过则做后续工作,否则直接拒绝请求;

用一张图来表示:

jwt设置有效期为永久 java jwt怎样设置过期_敏感数据

JWT其实分为JWS和JWE两种实现方式。JWS更加轻量,保证数据完整性,但是不保证安全性,所以适合token校验,传输非敏感数据;JWE算法相对复杂,安全性更高,适合传输文件或敏感数据;
一篇文章带你分清楚JWT,JWS与JWE

egg-jwt基于koa-jwt2,koa-jwt2基于jsonwebtoken,jsonwebtoken基于jws

如果需要传输重要文件或者敏感数据,请不要使用egg-jwt!!!
如果需要传输重要文件或者敏感数据,请不要使用egg-jwt!!!
如果需要传输重要文件或者敏感数据,请不要使用egg-jwt!!!

使用egg-jwt:

安装 egg-jwt

npm i egg-jwt -S

配置plugin.js
jwt: {
    enable: true,
    package: "egg-jwt"
  },
配置config/config.default.js

注:jwt.sign(xxx,xxx,[options,xxx])方法中,options的默认设置可以在sign配置;

config.jwt = {
    secret: 'tinyblog20210502',
    enable: true, // 默认是关闭,如果开启,这会对所有请求进行自动校验;限定请求,请设置match做路径匹配
    match: /^\/api/, // 匹配的请求,会走jwt校验,否则忽略;例如登录接口需要被忽略;
    sign: {	//jwt.sign(***,***,[options,***])方法中,options的默认设置可以在这里配置;
      expiresIn: 10   //多少s后过期。actionToken.js中,jwt.sing(plyload,secret,{expiresIn:number})会被合并,调用时设置优先级更高;
    }
  }
生成jwt:actionToken.js
const Service = require('egg').Service
class ActionTokenService extends Service {
    async apply(uid) {
        const { ctx } = this
        return ctx.app.jwt.sign({
            data: {
                uid: uid
            },
        }, ctx.app.config.jwt.secret)
    }
}
module.exports = ActionTokenService

在app下自动挂载jwt.sign()方法,用于生成jwt,配置如下:

jwt.sign(payload, secretOrPrivateKey, [options, callback])

options配置如下:

  • algorithm:加密算法(默认值:HS256)
  • expiresIn:多少时间后到期。以秒表示或描述时间跨度zeit / ms的字符串。如60,“2 days”,“10h”,“7d”,Expiration time,过期时间
  • notBefore:以秒表示或描述时间跨度zeit / ms的字符串。如:60,“2days”,“10h”,“7d”
  • audience:Audience,观众
  • issuer:Issuer,发行者
  • jwtid:JWT ID
  • subject:Subject,主题
  • noTimestamp
  • header

secretOrPrivateKey秘钥

  • secretOrPrivateKey:秘钥(包含HMAC算法的密钥或RSA和ECDSA的PEM编码私钥的string或buffer)。

pyload配置如下:

  • data:数据,一般可以放用户id、角色等这些非敏感数据
  • exp:到期时间,单位:秒。expiresIn:多少秒后过期;exp:是个时间节点;注意两者区别;payload.exp和options.expiresIn不能同时设置;
  • nbf:类似options.notBefore
  • aud:类似options.audience
  • sub:类似options.subject
  • iss:类似options.issuer
前端请求时,带上jwt口令

前端拿到jwt口令后,缓存到本地:

localStorage.setItem("my-token",jwt 口令)

每次请求时,附带jwt:

service.interceptors.request.use(
    config => {
        if (store.getters.token) {
            config.headers['Authorization'] = 'Bearer ' + getToken()//忘了谁都别忘了我
        }
        return config;
    },
    err => {
        return Promise.reject(err);
    }
);

注意:请求头中kwt的字段名称必须是“Authorization”

jsonwebtoken中文文档

错误拦截

egg文件加载顺序:

jwt设置有效期为永久 java jwt怎样设置过期_jwt设置有效期为永久 java_02


plugin.js插件第一个加载,所以egg-jwt插件默认是在洋葱圈表皮;

所以当一个请求发送到服务器时,会先经过egg-jwt,然后再经过middleware中的各个中间件。这样就存在一个问题:如果项目定义了全局的错误拦截中间件,那么将拦截不到egg-jwt校验失败时的错误(默认返回401状态码)。

我们需要将错误拦截中间件,提前到app初始化阶段,需要使用app.js中的生命周期:

//app.js
class AppBootHook {
    constructor(app) {
        this.app = app;

        //载入错误拦截中间件;(如果安装了路径别名工具,可以直接"@/middleware/error_handler.js")
        const errorHandle = require("./app/middleware/error_handler.js")({}, this.app);
        this.app.use(errorHandle);
    }
}

这里就是在实例化阶段,直接载入错误处理中间件,确保自身和后面所有中间件的报错都可以被拦截到,做统一处理再返回;