Nodejs之egg基本使用(egg服务、egg-mysql、CSRF漏洞、egg-jwt、Swagger)

本文介绍了Node.js中的Egg.js框架,详细讲解了如何进行数据库操作,如CRUD操作,使用egg-mysql模块。同时,文章探讨了API的安全问题,包括CSRF防护和JWT身份验证。还提到了Swagger用于接口文档的管理和Swagger的使用方法,以及CORS设置以解决跨域问题。
摘要由CSDN通过智能技术生成

Node系列文章目录

第一章:Node.js与内置模块(fs文件系统、path路径模块、http服务器模块)

第二章:Nodejs模块化(npm与包、开发自己的包、模块加载机制)

第三章:Nodejs之Express(基本使用、Express路由)(一)

第四章:Nodejs之Express( Express 中间件、中间件的分类、自定义中间件)(二)

第五章:Nodejs之解决接口跨域问题

第六章:Nodejs操作Mysql数据库

第七章:Node之前后端身份认证(Session认证机制、JWT 认证机制)

第八章:Nodejs之egg基本使用(初始化项目、内置对象、egg路由、egg控制器)



一、egg服务

简单来说,Service 就是在复杂业务场景下⽤于做业务逻辑封装的⼀个抽象层,提供这个抽象有以下⼏个好处:

  • 保持 Controller 中的逻辑更加简洁。
  • 保持业务逻辑的独⽴性,抽象出来的 Service 可以被多个 Controller重复调⽤。
  • 将逻辑和展现分离,更容易编写测试⽤例。
// app/services/student.js 该示例中的sql相关在egg-mysql章节专⻔有讲解。
const { Service } = require('egg')
class StudentService extends Service {
 async findById(id) {
	let sql = "select * from tbl_student where id = ?"; //问号代表占位符,之后被query⽅法的第⼆个参数数组中的数据填充
	 let student = await this.app.mysql.query(sql, [id]);
	 return student;
 }
 // student {id,name,gender}
 async saveOrUpdate(student) {
	 let result = null;
 if (student.id) {
 	result = await this.app.mysql.update('tbl_student', student)
 } else {
 	result = await this.app.mysql.insert('tbl_student', student)
 }
 	return result;
 }
 async deleteById(id) {
	let result = await this.app.mysql.delete('tbl_student', { id
})
 }
}
module.exports = StudentService;

服务的使⽤: 服务⼀般被controller使⽤。在controller中⽤ctx.services.student.findById(1)来访问service中的⽅法。
如下完整例⼦
app/router.js

module.exports = ( app) =>{
app.router.get('/student/findAll ', controller.student.findAll);
};

app/controller/student.js

const Controller = require('egg').Controller;
class StudentController extends Controller {
 async findAll() {
 const student = await this.ctx.service.student.findAll();
 this.ctx.response.body = student;
 }
}
module.exports = StudentController;

app/service/student.js

const Service = require('egg').Service;
class StudentService extends Service {
 async findAll() {
 let sql = 'select * from tbl_student'
 let result = await this.app.mysql.query(sql);
 return result;
 }
}
module.exports = StudentService;
// curl http://127.0.0.1:7001/student/findAll

在这里插入图片描述

二、egg-mysql

egg-mysql: https://www.npmjs.com/package/egg-mysql

2.1 安装

$ npm install --save egg-mysql

2.2 配置

config/plugin.js

'use strict';
/** @type Egg.EggPlugin */
module.exports = {
 mysql: {
 enable: true, package: 'egg-mysql',
 }
};

在这里插入图片描述
config.default.js

const userConfig = {
	 // myAppName: 'egg',
	 mysql: {
	 	// 单数据库信息配置
		 client: {
			 // host
			 host: '127.0.0.1',
			 user: 'zs',
			 password: '123456',
			 database: 'table',
			 port: '3306',
		 },
		 // 是否加载到 app 上,默认开启
		 app: true,
		 // 是否加载到 agent 上,默认关闭
		 agent: false,
		 }
};

在这里插入图片描述

2.3基本使用

service/student.js

const { Service } = require('egg')
class StudentService extends Service {
 async findById(id) {
 let student = await this.app.mysql.query('select * from tbl_student where id = ? ', [id])
 return student;
 }
}
module.exports = StudentService;

2.4 快捷⽅法

在egg中可以编写 CRUD 语句来完成增删改查。
CRUD: 增加(Create)、检索(Retrieve)、更新(Update)和删除(Delete)

  • insert(table,{}) 插入
    table为表名,{} 为插⼊的字段与值的映射对象

    mysql.insert('tbl_student', { name: "terry", gender: "男" })
    // 与该条sql语句的结果⼀致:insert into tbl_student (name,gender)
    values("terry","男")
    
  • get(table,{}) 查询
    table为表名,{}为where条件

    mysql.get('tbl_student', { id: 1 })
    // 与该条sql语句的结果⼀致:select * from tbl_student where id = 1;
    
  • select(table,{}) 查询
    table为表名,{} 为对象,对象中包含了where、columns、orders、limit、offset等属性

    // 查询全表的数据
    const results = await this.app.mysql.select('tbl_student');
    // 与该条sql语句的结果⼀致:select * from tbl_student;
    // 条件查询和结果定制
    mysql.select('tbl_student', { //查询表tbl_student
     where: { gender: "男" }, // WHERE 条件
     columns: ['name', 'gender'], // 要查询的表字段
     orders: [['id', 'desc'], ['name', 'desc']], // 排序⽅式
     limit: 10, // 返回数据量
     offset: 0, // 数据偏移量
    })
    /* 与该条sql语句的结果⼀致:
    	select name,gender
    	from tbl_student
    	where gender="男"
    	order by id desc, name desc
    	limit 0,10;
    */
    
  • update(table,{}[,options]) 更新
    table为表名,{}为更新后的值,默认根据id来更新

    mysql.update('tbl_student', { id: 1, name: "terry", gender: '⼥' })
    mysql.update('tbl_student', { name: "terry", gender: '⼥' }, {
     where: { id: 1 }
    })
    /* 与该条sql语句的结果⼀致:
    update tbl_student
    set name = 'terry',gender = '⼥'
    where id = 1;
    */
    
  • delete(table,{}) 删除
    table为表名,{}为删除条件

    mysql.delete(tbl_student, { id: 1 })
    // 与该条sql语句的结果⼀致:delete from tbl_student where id = 1;
    

三、CSRF漏洞

在egg框架中,访问get⽅式的接⼝没有CSRF漏洞,但是访问post/put/delet⽅式的接⼝有CSRF漏洞。

CSRF(Cross-site request forgery)简称: 跨站请求伪造,攻击者通过伪造⽤户的浏览器的请求,向访问⼀个⽤户⾃⼰曾经认证访问过的⽹站发送出去,使⽬标⽹站接收并误以为是⽤户的真实操作⽽去执⾏命令。攻击者利⽤⽹站对请求的验证漏洞⽽实现这样的攻击⾏为,⽹站能够确认请求来源于⽤户的浏览器,却不能验证请求是否源于⽤户的真实意愿下的操作⾏为。

造成的问题包括: 个⼈隐私泄露以及财产安全。

  • 防御CSRF攻击

⽬前防御 CSRF 攻击主要有三种策略:

  • 验证 HTTP Referer 字段;
  • 在请求地址中添加 token 并验证;
  • 在 HTTP 头中⾃定义属性并验证。

在项⽬中如果报csrf漏洞问题,⽬前咱们可以先关闭csrf漏洞检测,后期由前端携带token来解决:

// config.default.js
security: {
 csrf: {
 enable: false,
 },
},

在这里插入图片描述

四、egg-jwt

egg-jwt插件⽹站:https://www.npmjs.com/package/egg-jwt

4.1 安装

$ npm install egg-jwt --save

4.2 配置

plugins.js

jwt : {
 enable: true,
 package: 'egg-jwt',
},

config.default.js

jwt:{
 secret:"888888"//加密秘钥
},

在这里插入图片描述
在这里插入图片描述

3.3 使⽤

使⽤jwt进⾏⽤户身份认证⾮常⽅便。

  • sign()

此⽅法⽤来⽣成token信息,在登录接⼝中,验证⽤户身份通过之后,才去⽣成token。其语法格式如下:

// 签发token
this.app.jwt.sign(payload, secretOrPrivateKey, [options, callback])
// 示例
const token = 'Bearer ' + this.app.jwt.sign({ username: param.username}, 
this.app.config.jwt.secret,{ expiresIn: '60s' } );

参数说明
1.payload 为⼀个对象,后期可以根据token解析出这个对象的信息
2.secretOrPrivateKey 秘钥
3.options配置对象 {expiresIn : ‘60s’} token有效期

  • verify()
    此⽅法⽤来核实token,可通过此⽅法获取token上携带的头部的信息可使⽤如下代码来通过token获取⽤户信息:
const token = this.ctx.get('Authorization').substring(7);
// 验证token,反编译获取username
const { username } = this.app.jwt.verify(token,this.app.config.jwt.secret)
  • 路由添加jwt身份认证
    添加了jwt身份认证的接⼝在访问时,请求时在header中配置 Authorization =Bearer ${token} 。在需要身份认证的接⼝上添加jwt,需要在router.js中配置需要jwt认证的路由,添加了jwt认证的路由前端访问的时候,就需要在请求头部携带Authorization = Bearer ${token} 信息了。

router.js

const { router, controller, jwt } = app;
// 给路由添加需要token认证
router.get('/student/findAll', jwt, controller.student.findAll);
router.post('/student/saveOrUpdate', jwt, controller.student.saveOrUpdate)

五、Swagger

官⽹:https://swagger.io/
egg-swagger-docs:https://www.npmjs.com/package/egg-swagger-docs

5.1 安装

$ npm install egg-swagger-docs --save

5.2 配置

  • config/plugin.js内添加如下插件信息
swaggerdoc : {
 enable: true,
 package: 'egg-swagger-docs',
},

在这里插入图片描述

  • config/config.default.js ⽂件内的userConfig对象内添加如下信息
swaggerdoc: {
	 dirScanner: "./app/controller", // 配置⾃动扫描的控制器路径
	 apiInfo: {
		 title: "xxx系统", // 接⼝⽂档标题
		 description: "xxx系统接⼝⽂档", // 接⼝⽂档描述
		  version: "1.0.0", // 接⼝⽂档版本
	 },
	 schemes: ["http", "https"], // 配置⽀持的协议
	 consumes: ["application/json","application/x-www-formurlencoded"], // 指定处理请求的提交内容类型(Content-Type)
	 produces: ["application/json"], // 指定返回的内容类型
	 securityDefinitions: { //安全认证
	 apikey: {
		 description: 'Authorization format: Bearer {token}', //描述
		 type: 'apiKey', //类型
		 name: 'Authorization', // 名称
		 in: 'header' //存在位置
	 }
	 },
	 enableSecurity: true, // 是否启⽤授权(默认false不启⽤)
	 // enableValidate: true, // 是否启动参数校验(默认true开启)
	 routerMap: false, // 是否⾃动⽣成路由(默认true开启)
	 enable: true,
},

在这里插入图片描述

完成插件引⼊之后,如果不修改默认配置,应⽤启动后,会⾃动扫描app/controllerapp/contract下的⽂件。controller下的⽂件先不做描述。contract下的⽂件为定义好的请求体和响应体

在这里插入图片描述
在这里插入图片描述

5.3 编写Swagger

Swagger的⽂档内容编写在⾃⼰声明的Controller的⽂件中去编写。

  • @Controller
    在类上⽅使⽤此注解。
    格式:@Controller {ControllerName}
    • 如果⽂件第⼀个注释块中存在标签@Controller,应⽤会扫描当前⽂件下的所有注释块,否则扫描将会跳过该⽂件。
    • 如果不标示ControllerName,程序会将当前⽂件的⽂件名作为ControllerName。
    /**
     * @Controller StudentController:学生模块
     */
    class StudentController extends Controller {
    }
    

在这里插入图片描述

  • @Router
    在Controller类内的⽅法上⽅使⽤此注解。
    格式:@Router {Mothod} {Path}
    Mothod:请求的⽅法(post/get/put/delete等),不区分⼤⼩写。
    Path:请求的路由。
    class StudentController extends Controller {
        /**
         * @Router get /student/findAll
         */
        // 查询接口处理函数
        async findAll() {
        };
    

在这里插入图片描述

  • @Summary
    在Controller类内的⽅法上⽅使⽤此注解。
    格式:@Summary {Summary}
    接⼝信息⼩标题
	class StudentController extends Controller {
	    /**
	     * @Summary 查询学生模块
	     */
	    // 查询接口处理函数
	    async findAll() {
	    };

在这里插入图片描述

  • @Description
    在Controller类内的⽅法上⽅使⽤此注解。
    格式:@Description {Description}
    接⼝具体描述
class StudentController extends Controller {
    /**
     * @Description 查询所有学生不需要传参
     */
    // 查询接口处理函数
    async findAll() {
    };

在这里插入图片描述

  • @Request
    在Controller类内的⽅法上⽅使⽤此注解。
    格式:@Request {Position} {Type} {Name} {Description}
    • position:参数的位置,该值可以是body/path/query/header/formData。
    • Type:参数类型,body之外位置⽬前只⽀持基础类型,integer/string/boolean/number,及基础类型构成的数组,body中则⽀持contract中定义的类型。如果position是formData还将⽀持file类型。
    • Name 参数名称.如果参数名称以*开头则表示必要,否则⾮必要。
    • Description:参数描述
      如果你想给query或者path的参数设置example,你可以在Description前添加
      以’eg:'开头的参数,实例如下 @Request query string contactId eg:200032234567
      顾问ID

body参数

如果写了@Request body,则可以在contract⽂件夹中声明请求体的模板。

controller/user.js:

class UserController extends Controller {
    /**
     * @Router post /user/login
     * @Summary 用户登录模块
     * @Description 传参username和password为必传参数
     * @Request body loginVM 
     */
    // 登录接口
    async login() {
    }

contract/user.js

module.exports = {
    // 对请求体参数模块
    loginVM: {
        username: {
            type: "string",
            require: true,
            example: "admin"
        },
        password: {
            type: "number",
            require: true,
            example: "123321"
        }
    }
}

在这里插入图片描述

query参数
必填项在名称前边加*

/**
 * @Router delete /student/deleteById
 * @Summary 通过id删除学⽣信息
 * @Description 详细描述信息
 * @Request query number *id 需要删除的id值
 */
 async deleteById() {
 ...
 }

在这里插入图片描述
多个query参数

/**
 * @Router get /student/pageQuery
 * @Summary 分⻚查询学⽣信息
 * @Description 详细描述信息
 * @Request query number page ⻚码
 * @Request query number pageSize 每⻚条数
 */
async pageQuery() {
 ...
}

在这里插入图片描述

@apikey身份认证
如果在config中开启并定义了securityDefinitions,默认enableSecurity为false。则可
在注释块中加⼊@apikey,加⼊安全验证。也可定义成其他名字,只需@定义好的
字段名就好。关于securityDefinitions的定义可以⾃⾏搜索

swaggerdoc : {
 securityDefinitions: {
 apikey: { //这⾥的apikey代表注解的名称,如果这⾥改为apikey2,则可以使⽤@apikey2来携带身份信息
	 type: 'apiKey',
	 name: 'clientkey',
	 in: 'header',
 },
 },
 enableSecurity: true,
},

如果egg项⽬内使⽤了egg-jwt做了身份认证,并在配置路由的时候给某个接⼝设置了jwt身份认证,那么需要使⽤该注解。否则有身份认证的接⼝在swagger中测试的时候,会报没有进⾏身份认证的错误。

{
 "code": "credentials_required",
 "message": "No authorization token was found"
}

路由注册:

const { router, controller, jwt } = app;
router.get('/student/findAll', jwt, controller.student.findAll);

路由@apikey注解:

/**
 * @Router get /student/findAll
 * @Summary 查询所有学⽣信息
 * @Description 详细描述信息
 * @apikey
 */
async findAll() {
 ...
}

六、Cors

egg-corshttps://www.npmjs.com/package/egg-cors

1.安装

npm install egg-cors --save

2.配置

config/plugin.js

cors:{
 enable: true,
 package: 'egg-cors',
},

在这里插入图片描述

config/config.default.js

cors: {
 origin: '*',
 allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
},

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值