本文最后更新于:2023年3月19日 晚上
前言
本文将基于 Egg.js,使用 Typescript,实现基于 JWT 的 RESTful API。使用到的插件有:egg-mysql
、egg-sequelize
、egg-validate-joi
、egg-jwt
、egg-cors
、egg-router-plus
、egg-swagger-doc
项目源码请访问:https://github.com/HaisawaEtsu/egg-test-demo
相关插件配置
插件安装
相关 ts 目录规范、插件安装及配置教程请参考官网及 npmjs 文档:
egg-typescript
egg-mysql
egg-sequelize
RESTFul API
egg-validate-joi
egg-router-plus
egg-swagger-doc
egg-jwt
1.使用 npm 安装egg-cors
及egg-jwt
1
| npm install egg-cors egg-jwt --save
|
2.config/plugin.ts
中添加相应的配置
1 2 3 4 5 6 7 8 9 10 11
| ... jwt: { enable: true, package: 'egg-jwt', }, cors: { enable: true, package: 'egg-cors', }, ...
|
3. config/config.default.ts
添加相应的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ... config.jwt = { secret: 'r1Wp3kxj3TmaUrruPwyZzNgkaxepMHyo', };
config.security = { csrf: { enable: false, ignoreJSON: true, }, domainWhiteList: [ 'http://localhost' ], }; config.cors = { origin: '*', allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH', }; ...
|
配置文件
config/plugin.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import { EggPlugin } from "egg";
const plugin: EggPlugin = { static: true, mysql: { enable: true, package: "egg-mysql", }, sequelize: { enable: true, package: "egg-sequelize", }, validate: { enable: true, package: "egg-validate", }, jwt: { enable: true, package: "egg-jwt", }, cors: { enable: true, package: "egg-cors", }, routerPlus: { enable: true, package: "egg-router-plus", }, swaggerdoc: { enable: true, package: "egg-swagger-doc", }, }; export default plugin;
|
路由及路由映射建立
使用egg-router-plus进行路由拆分,这个插件会自动读取app/router
下的分路由 ts 文件
1 2 3 4 5 6 7 8 9 10 11
| import { Application } from "egg";
export default function (app: Application) { const { controller, router } = app; router.resources( "authorization", "/api/v1/authorization", controller.authorization ); }
|
再需要权限验证的路由,引入 jwt 中间件
1 2 3 4 5 6 7
| import { Application } from "egg";
export default function (app: Application) { const { controller, router, jwt } = app; router.resources("user", "/api/v1/user", jwt, controller.user); }
|
登录鉴权接口的实现
Controller 及 Service 的编写
本项目中同时引入了egg-swagger-doc建立 api 接口文档,具体前置操作可以查看文档。
Controller
使用egg-swagger-doc
,可以使用 jsDoc 的形式,结合egg-swagger-doc
在contract
中定义的规则,可以在页面中自动生成接口文档相关信息。
同时使用了/extend/helper.js/
统一封装了正确及错误返回方法,使用ctx.helper.success
以及ctx.helper.fail
来调用,具体实现请查看源码。
使用了egg-validate-joi
进行参数合法校验,如果参数不合法使用ctx.helper.validateError
返回错误结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import { Controller } from "egg";
export default class AuthorizationController extends Controller {
public async create() { const { ctx, app } = this; const { loginName, password } = ctx.request.body; const { Joi } = app; const errors = ctx.validateJoi({ body: { loginName: Joi.string().required(), password: Joi.string().required(), }, }); if (errors) { ctx.helper.validateError(ctx, errors); return; } const validUser = await ctx.service.user.validUser(loginName, password); if (validUser.isValid) { const { user } = validUser; const token = app.jwt.sign( { username: user.username, u_id: user.u_id, }, app.config.jwt.secret );
ctx.helper.success(ctx, "ok", { token }); } } }
|
Service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| import { Service } from "egg"; import crypto = require("crypto");
type validUser = { isValid: boolean; user?: any; };
export default class UserService extends Service { public async validUser( loginName: string, password: string ): Promise<validUser> { const { ctx } = this; const user = await this.findByLoginName(loginName);
if (user) { const pwd = crypto.createHash("md5").update(password).digest("hex"); const u = user.get(); if (u.password === pwd) { return { isValid: true, user: u, }; } ctx.helper.fail(ctx, { code: 0, msg: "密码错误", }); return { isValid: false, }; } ctx.helper.fail(ctx, { code: 0, msg: "用户名不存在", }); return { isValid: false, }; }
public async findByLoginName(loginName: string) { const { ctx } = this; const res = await ctx.model.User.findOne({ where: { login_name: loginName, }, }); if (res) { return res; } return null; }
public async findByUsername(username: string) { const { ctx } = this; const res = await ctx.model.User.findOne({ where: { username, }, }); if (res) { return res; } return null; } }
|
使用 swagger 进行测试
默认地址为http://localhost:7001/swagger-ui.html