最新消息:看到那些跳动的图片、文字了吗?点击点击 O(∩_∩)O~~

TypeScript 环境使用 egg-jwt

若思若想 onlyling 3308浏览

JWT

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。

部分容器/场景无法使用 cookie,JWT 就是一个不错的选择。

基础配置

## 安装依赖
yarn add egg-jwt

config/config.default.ts

import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg';

export default (appInfo: EggAppInfo) => {
  const config = {} as PowerPartial<EggAppConfig>;

  // override config from framework / plugin
  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1589209631587_7631';

  // add your egg config in here
  // 统一处理错误
  config.middleware = ['errorHandler'];

  // jwt
  // 参数参考:https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback
  config.jwt = {
    secret: '1234567890', // secretOrPublicKey
  };

  // add your special config in here
  const bizConfig = {
    // sourceUrl: `https://github.com/eggjs/examples/tree/master/${appInfo.name}`,
  };

  // the return config will combines to EggAppConfig
  return {
    ...config,
    ...bizConfig,
  };
};

config/plugin.ts

import { EggPlugin } from 'egg';

const plugin: EggPlugin = {
  // static: true,
  // nunjucks: {
  //   enable: true,
  //   package: 'egg-view-nunjucks',
  // },

  // JWT
  jwt: {
    enable: true,
    package: 'egg-jwt',
  },
};

export default plugin;

app/middleware/jwt.ts

import * as koajwt from 'koa-jwt2';

// 文档上这样使用,但是中间件类型不匹配
// app.get("/", app.jwt, "render.index");
module.exports = (options) => {
  return koajwt(options);
};

app/middleware/error_handler.ts

import { Context } from 'egg';

module.exports = () => {
  return async function errorHandler(ctx: Context, next) {
    try {
      await next();
    } catch (err) {
      // 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
      ctx.app.emit('error', err, ctx);

      const status = err.status || 500;
      // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息
      const error =
        status === 500 && ctx.app.config.env === 'prod' ? 'Internal Server Error' : err.message;

      // 从 error 对象上读出各个属性,设置到响应中
      ctx.body = ctx.helper.APIFail(error, status);

      if (status === 422) {
        ctx.body.detail = err.errors;
      }
      ctx.status = status;
    }
  };
};

使用

路由配置

import { Application } from 'egg';

/**
 * 构建路径
 * @param p 路径
 */
const buildPath = (p: string) => {
  return `/api/app/${p}`;
};

export default (app: Application) => {
  const { controller, router } = app;
  // jwt 中间件,把请求头部带的 Authorization 转换成具体的数据
  const jwt = app.middleware.jwt(app.config.jwt);

  router.post(buildPath('user/login'), controller.app.user.PostLogin);

    // 退出登录、清除 token
    // https://github.com/okoala/koa-jwt2/blob/master/lib/index.js#L124
    // isRevokedAsync 函数默认返回 options.isRevoked
    // 清除 token 会抛出 revoked_token 错误
    // https://github.com/okoala/koa-jwt2/blob/master/lib/errors/UnauthorizedError.js
    router.get(
    buildPath('user/logout'),
    app.middleware.jwt({
      ...app.config.jwt,
      isRevoked: true,
    })
  );

  router.post(buildPath('micro-blog'), jwt, controller.app.user.PostMicroBlog);
};

控制器中使用

import { Controller } from 'egg';

export default class UserController extends Controller {
  /**
   * 用户登录;
   * {string} user_name 用户名
   * {string} password 密码
   */
  public async PostLogin() {
    const { ctx, app } = this;
    const { body } = ctx.request;

    // 用户的登录业务

    // 登录成功
    // 生成 token
    const token = app.jwt.sign(
      {
        id: user.id,
        user_name: user.user_name,
      },
      app.config.jwt.secret,
      {
        expiresIn: '24h',
      },
    );

    ctx.body = ctx.helper.APISuccess({
      token,
    });
  }

  // 请求头注意,Authorization: Bearer xxxxxx
  // 注意需要在上面生成的 token 上添加 `Bearer `,完整的数据是 `Bearer ${token}`
  // 部分场景访问接口可能没有 token,koa-jwt2 会在应用内抛出一个错误,需要一个统一处理错误的中间件
    public async PostMicroBlog() {
    const { ctx } = this;
    // ctx.state.user 上会有对应的数据
    ctx.body = ctx.helper.APISuccess(ctx.state.user);
  }
}

转载请注明:OnlyLing - Web 前端开发者 » TypeScript 环境使用 egg-jwt