WEB后端向浏览器前端提供服务是最常见的场景,前端向后端的接口发起GET或者POST请求,后端收到请求后执行服务器端任务进行处理,完成后向前端发送响应。
那浏览器前端向后端提供服务是什么鬼?
说来话长,长话短说。我在人脸识别场景用了开源的face-api.js。它其实有浏览器端的完整模块,但是我觉得人脸识别在实际场景中应用,全部在前端做识别不太安全,因为浏览器侧未必完全安全可控,可以考虑人脸登记注册时前端将照片和用户绑定信息发到后端,后端识别出描述信息保存,识别阶段,浏览器调用摄像头识别出人脸描述信息,发往后端,在后端进行匹配验证,这样会更安全可靠一些。但是出现了一个状况,后端依赖库中有部分依赖不翻墙是下不下来。。。于是我想了一个变通的办法,人脸匹配在一个“可信前端”上执行,后端向它传输必要数据,它执行匹配判断,然后向后端传回匹配结果。思路有了,那怎么实现呢?
解决方案就是websocket,它支持双向通信。服务器上启动websocket server后,可信客户端上浏览器访问建立websocket连接,可以通过网络访问权限和服务器对请求源IP的识别来限制仅该浏览器端可以与服务器建立websocket连接。websocket的请求响应机制跟http的请求响应机制略有不同,将用户请求的响应放在websocket的消息接收处理中了。
简单示例如下:
const express = require('express');
const app = express();
var wsServer = require('express-ws')(app)
const stringRandom = require("string-random");
var ress=[];
app.use('/', express.static('./'));
app.ws('/ws', (ws,req)=>{
console.log(req.ip);
ws.send(JSON.stringify({"msg":"websocket connected!"}));
ws.on('message', msg => {
jmsg=JSON.parse(msg);
if (jmsg.type=="answer") {
let res1=ress.find(item=>(item.ref==jmsg.res))
res1["res"].json(JSON.stringify({"code":200,"msg":jmsg.msg}));
}
else console.log(msg);
})
});
app.post('/inq', express.json(),(req, res) => {
const question = req.body.question;
let refcode=stringRandom(32, { letters: 'ABCDEF' });
ress.push({"ref":refcode,"res":res});
Array.from(wsServer.getWss().clients)[0].send(JSON.stringify({"type":"ask","msg":question,"res":refcode}));
});
app.listen(3000, () => { console.log(`express后端查询websocket demo启动`); })
浏览器地址栏输入http://localhost:3000/trust.html
<!DOCTYPE html>
<html>
<head>
<title>websocket前端充当查询服务器</title>
<meta charset="utf-8">
<script src="jquery-3.6.1.min.js"></script>
</head>
<body>
<script>
var ws = new WebSocket("http://localhost:3000/ws");
ws.onmessage = event => {
let jvar=JSON.parse(event.data);
console.log(`收到查询: ${jvar.msg}`)
if (jvar.type=="ask") {
ws.send(JSON.stringify({"type":"answer","res":jvar.res,"msg":"yes"}))
}
}
</script>
</body>
</html>
Postwoman里POST请求http://localhost:3000/inq
该例子简单,html的js里处理消息type为"ask"的段内增加些代码就可满足更复杂的计算逻辑处理了,比如上面提到的人脸匹配,消息传入里question可以是一个object,比如包含待识别的人脸描述符,和已注册的人脸描述特征等。
这种变通的前后端模式既规避了纯前端方案的安全问题,又绕开了后端依赖模块缺失的问题,虽然比较另类,但是能解决问题就好。