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

  • Post author:
  • Post category:mysql




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/controller



app/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-cors



https://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'
},

在这里插入图片描述



版权声明:本文为qq_48617322原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。