提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
基于Nest.js+Mongodb+定时任务实现发送邮件功能(进行接口鉴权)
现实开发中,我们可能会遇到参数被篡改的情况,所以一般一些重要的接口都会进行签名校验,实现过程是前端和后端进行统一的签名方式和秘钥去生成签名,后端对比签名是否一致,不一致则直接拒绝访问,不再进行操作。
一、签名是什么?
为什么需要 API 接口签名
1.保证请求数据正确
当请求中的某一个字段的值变化时,原有的签名结果就会发生变化。所以,只要参数发生变化,签名就要发生变化,否则请求将会是一个无效的请求。
2.保证请求来源合法
一般情况下,生成签名的算法都会成对出现一个 appKey 和一个 appSecret,根据 appKey 能识别出调用者身份;根据 appSecret 能识别出签名是否合法。
3.识别接口的时效性(设计个过期时间 )
一般情况下,签名和参数中会包含时间戳,这样服务端就可以验证客户端请求是否在有效时间内,从而避免被人恶意刷接口。
二、步骤
1.引入中间件
提示:当然使用别的也可以。nestjs中提供(中间件、过滤器、守卫、管道、拦截器)供我们使用
代码如下(示例):首先说一下我的思路,就是前端通过与后端相同的加密思路加密签名
将他们作为信息传入后端接口,通过中间件处理 签名有效放行,签名无效401
代码实现
import {
Injectable,
NestMiddleware,
Param,
Get,
Query,
Req,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import * as crypto from 'crypto';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class EmailMiddleware implements NestMiddleware {
constructor(private configService: ConfigService) {}
private secret = '123456'; //密钥 不能硬编码
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
//通过获取外部配置文件的方式解决硬编码问题
const appkey = this.configService.get<string>('database.appkey');
const dbHost = this.configService.get<string>('database.host', 'localhost');
const zhengxifeng = this.configService.get<string>('ZXF');
console.log(zhengxifeng + 'zhengxifeng');
console.log(appkey + '从.env文件中获取的');
console.log(dbHost + '从.env文件中获取的');
const signature = req.headers['x-singsture']; //获取签名值
const timestamp = req.headers['x-timestamp'] as string; //时间戳
const nonce = req.headers['x-nonce']; //请求头获取随机数
const params = req.query; //获取url后面?的信息 (查询参数)
const params1 = req.body; //获取请求体信息 (post请求)
console.log('时间戳');
console.log(timestamp);
console.log('当前时间');
console.log(Date.now());
console.log('随机数');
console.log(nonce);
console.log('post中的信息');
console.log(params1);
console.log('******************************');
console.log(signature);
console.log('******************************');
const dateInt = parseInt(timestamp);
console.log('前端时间戳信息' + dateInt);
if (Date.now() - dateInt > 1200000000) {//我是用的简单的判断
//200分钟有效
console.log('datade 信息' + Date.now());
throw new HttpException('请求已过期', HttpStatus.UNAUTHORIZED);
}
//
// const tt = //秒
const paramList = Object.keys(params1)
.sort()
.map((key) => `${key}=${params1[key]}`);
console.log('将请求携带的参数转换List');
console.log(paramList);
const paramString = paramList.join('&');
console.log('拼接');
console.log(paramString);
const message = `${timestamp}${nonce}${paramString}`;
console.log('将时间戳随机数请求信息转换成大的字符串');
console.log(message);
// 使用密钥对待签名的字符串进行HMAC-SHA256签名
const hmac = crypto.createHmac('sha256', appkey);
const signatureComputed = hmac.update(message).digest('hex');
console.log('计算得到的签名');
console.log(signatureComputed);
// 验证签名是否正确
if (signature === signatureComputed) {
next(); // 签名验证通过,继续处理请求
} else {
res.status(401).send('Unauthorized'); // 签名验证失败,返回401错误
}
}
}
postman调用