在前后端分离,前端使用Axios 或者是Ajax 来发送数据和接收数据,对应非程序人来说就没有有,应为他们也不懂,对应懂程序地来说 直接F12 查看浏览器地请求(Network)来查看请求数据,和返回数据,对应我们本身开发者来说无疑是在当中众多同行种裸奔
为了防止数据传输过程中裸奔 进行数据加密传输
前端发送数据地时候 进行数据加密,
每一个发送数据都要加密?
这个看自己怎么对业务请求代码地封装了(建议二次封装请求,方便传输时候进行统一地加密)
什么时候对返回数据地解码?
(在二次封装请求数据)在数据回来地时候对数据解码能够统一,全局也只写一次,
前端加密核心代码 使用ES6语法创建类,加密和解密类 (前后端采用同方法和函数)
class unscrambler {
constructor(setDecode, getDecode) {
Object.assign(this, {
setDecode,
getDecode
});
}
}
// 设置
const setDecode = setObj => {
if (Object.keys(setObj).length > 0) setObj = JSON.stringify(setObj);
let set = encodeURIComponent(setObj),
result = btoa(set);
return result;
}
// 获取解密
const getDecode = getData => {
let jieMi = atob(getData),
jieM = decodeURIComponent(jieMi);
try {
return JSON.parse(jieM)
} catch (e) {
return false
}
}
let decode = new unscrambler(setDecode, getDecode);
前端 uniapp 请求封装
注意点
export default class RequestHttp {
static req(Interface, data, method = "POST", dataType = "json") {
// ==========数据的加密发送 S
let sendData = data && decode.setDecode(data);
// ==========数据的加密发送 E
let url = `${getState("api", "baseUrl")}${Interface}`;
let Authorization;
uni.getStorage({
key: 'userInfo',
success: (res) => {
Authorization = res.data.token
}
});
return new Promise((resolve, reject) => {
uni.request({
url,
data:sendData,
header: {
SelfSummerHeader: "hello", //自定义请求头信息
"content-type": 'application/xml',
Authorization
},
method,
dataType,
fail() {
showMessage("网络请求失败");
},
success(response) {
!response.data && showMessage("接口报错,报文无data数据");
if (response &&[200, 201, 204,304].includes(response.statusCode)) {
//=========================
//进行数据的解密
const receiveData = decode.getDecode(response.data);
//=========================
switch (receiveData.StatusCode) {
// 当请求返回是的状态码正确是才返回数据
case 200:
let data = receiveData.data;
resolve(data);
default:
showMessage('请求失败');
reject(response);
}
} else {
showMessage("网络异常");
}
},
});
});
}
}
后端
在什么时候进行传递过来地数据解码?
建议在接收到数据地时候解码
什么时候加密返回
发送数据就加密返回
在每一次API接口前面来写解码解码太麻烦,还不够统一,所有我们自定义中间件来接收数据
NodeJS自定义中间件,前面有文章讲解 NodeJS+Express 自定义中间件
接收数据在自定义中间件
前端传入是加密后的字符串,所有不能使用body-parser 插件(只能够自己写)
涉及到的文件上传使用了 multer 插件 下面定义中间件的时候要注意,否则会引发后面API接口不响应
接收收据要在 req.on 注册事件来接收 并且解密
把数据接收后再把数据直接挂载到req 上面,后续API接口直接可以在 req.bodyData 上面 就有传输的数据了
路由挂载
中间件挂载
设置跨域
导出配置到 server.ts 作为启动文件
var express = require("express"); //Express框架
var router = express.Router(); //Express 路由
const { systemOs, findSync,portIsOccupied } = require("../PunlicFunc/PublicFunction"); //公共封装的方法
var app = express();
var fs = require("fs");
const COMPort: number = 8978; //端口号
var join = require("path").join;
// 自定义Nodejs中间件
const middleware_router = require("../middleware/middleware_router");
app.listen(COMPort, (err: Error) => {
if (err) {
console.log('启动错误', err)
}else{
console.log('服务启动成功', COMPort)
// 挂载自定义中间件
app.use(middleware_router.bodyData,middleware_router.unifiedErr);
}
});
app.use(express.json()); //吧客戶端传递的参数解析为json 会导致Node服务接受不到请求
app.use(express.urlencoded({ extended: true }));
//设置跨域访问
app.all("*", (req: any, res: any, next: Function) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild"
);
res.header(
"Access-Control-Allow-Headers",
"content-type,SelfSummerHeader,Authorization"
); //自定义请求头
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", "3.2.1");
res.header("Content-Type", "application/json;charset=UTF-8");
res.header("Content-Type", "text/xml");
if (req.method === "OPTIONS" || req.url === "/favicon.ico") {
// 拦截请求
return res.send();
}
next();
});
// 路由挂载
app.use("/Mailbox", require("../../ServerApi/ExtendedServices/api.ts"));
//访问根目录需要干事
app.get("/", (res: any, req: any) => {
req.send("路径非法");
});
app.post("/", (res: any, req: any) => {
req.send("路径非法");
});
// 中间件集合
const middleware = { ...middleware_router };
/**
* 向外暴露 Express配置好的 框架主方法
*/
export {
app,
express,
router,
middleware,
};
Nodejs v16 版本后才支持 atob, btoa ,之前版本是不支持的使用 Buffer.from()函数代替
新建PublicFunction.ts用于公共方法
class unscrambler {
constructor(setDecode: Function, getDecode: Function) {
Object.assign(this, { setDecode, getDecode });
}
}
// 设置加密参数
const setDecode = (setObj: string) => {
try {
if (Object.keys(setObj).length > 0) setObj = JSON.stringify(setObj);
let set = encodeURIComponent(setObj), result = Buffer.from(set, 'binary').toString('base64');
return result;
} catch (error) {
return null
}
}
//解密参数
const getDecode = (getData: string) => {
let jieMi = Buffer.from(getData, 'base64').toString('binary'),
jieM = decodeURIComponent(jieMi);
try {
return JSON.parse(jieM)
} catch (e) {
return null
}
}
let decode = new unscrambler(setDecode, getDecode);
/**
* 获取目录下面所有的文件名称以及路径
* @param startPath String
* 返回指定目录下面的文件路径
*/
function findSync(startPath: String): Array<Object> {
let result: Array<Object> = [];
let finder = (path: String) => {
let files = fs.readdirSync(path);
files.forEach((val: string, index: number, list: Array<string>) => {
let fPath = join(path, val), stats = fs.statSync(fPath);
if (stats.isDirectory()) finder(fPath);
if (stats.isFile()) {
let add = fPath?.replace(/\\/g, "/");
result.push({
FileUrl: add,
paths: add.split("/"),
FileName: val,
type: new mailboxSuffix().int(val, "."),
});
}
});
}
finder(startPath);
return result;
}
export { findSync,decode };
const { decode } = require("../PunlicFunc/PublicFunction"); //公共封装的方法
const bodyData = (req: { body: object, query: object, bodyData: object, on: Function, decode: object, files: Array<Object> }, res: any, next: Function) => {
req.decode = decode;
res.sendSuccess = (data: any, StatusCode?: string, msg: String = '成功') => {
if (Object.keys(data).length === 3) res.send(decode.setDecode({ ...data }))
try {
let news = null, code = null;
if (isType(StatusCode) === String) {
news = StatusCode;
code = 200;
} else {
news = msg;
code = StatusCode || 200;
}
res.send(decode.setDecode({ StatusCode: code || StatusCode, data, msg: news || msg }));
} catch (error) {
}
}
res.sendFail = (StatusCode: number = 400, msg: String, err: Error) => res.send(decode.setDecode({ StatusCode, msg, err }));
let str = '';
req.on("data", (dt: string) => {
str += dt
})
req.on("end", () => {
let JSONS = decode.getDecode(str);
req && (req.bodyData = { ...JSONS });
next instanceof Function && next();
})
if (Array.isArray(req.files)) next instanceof Function && next();
};
发送数据也在同一个中间件里面进行加密房啊的挂载,后面每一个路由里面用自定义的函数进行返回数据,并且返回的数据是加密了的
新建api.ts
router.all("/login", async (req: { query: Object, bodyData: { uerMail: string; uerPwd: string }, decode: any }, res: any,next:Function) => {
const { uerMail, uerPwd } = req.bodyData;
let lookup = await db('SELECT us.uerid , us.usermali FROM `userinfo` as us WHERE usermali = "' + uerMail + '"');
if (Array.isArray(lookup) && lookup.length === 0) return res.sendFail(240, lookup)
let data = await db('SELECT us.uerid , us.usermali,us.nickname,us.msg,us.img FROM `userinfo` as us WHERE usermali = "' + uerMail + '"and userpwd ="' + uerPwd + '"')
if (data.length === 1) {
const { uerid, usermali } = data[0];
let token = new toke_Jwt().generateToken({
uerid,
usermali,
});
return res.sendSuccess({ ...data[0], token }, "登录成功");
} else {
return res.sendFail(260);
}
}
);
可以在req 上面看到我们自定的req.bodyData对象,里面是解密后前端传递过来的参数
使用 crypto-js
npm i crypto-js
crypto-js 可以在任何js环境使用,包含Node.js 都可以使用
注意如果在小程序环境使用,记得使用 4 以下的版本否则会出错
npm i [email protected]
数据加密
import CryptoJS from 'crypto-js'
data = CryptoJS.AES.encrypt(JSON.stringify(data), '自定义加密字符出key').toString();
consloe.log(data)
数据解密
import CryptoJS from 'crypto-js'
let bytes = CryptoJS.AES.decrypt(getData, '自定义加密字符出key'),
originalText = bytes.toString(CryptoJS.enc.Utf8);
console.log(originalText)
更多使用教程 crypto-js