最近领导要求防止在线3D模型被下载。查阅了网上关于这个话题的讨论,基本的回答是组件不能下载,只能添加下载的组件。难点,比如这篇文章,https://forum.babylonjs.com/t...。但是领导已经说了,很简单,不就是二进制数据吗,加密一下就可以了,只能先想办法了。目前能想到的方案很简单:服务器返回3D模型数据时,对返回内容进行加密。前端解密返回的内容。由于前端解密有性能需求,可以考虑使用WebAssembly+ServiceWorker来进行解密操作。如果对这两种技术不了解,可以自行查阅相关资料。初始化场景基于babylon.js,导入一个glb格式的模型,基于webpack构建一个简单的工程constcanvas=document.getElementById("canvas")asHTMLCanvasElement;constengine=newEngine(canvas);engine.setSize(window.innerWidth,window.innerHeight);constscene=newScene(engine);scene.createDefaultCameraOrLight(真,true,true);SceneLoader.AppendAsync("/static/models/","Xbot.glb").then((scene)=>{});functionrender(){engine.runRenderLoop(()=>{场景渲染();});}使成为();打开效果如图。使用Node.js加密模块进行服务器端加密。返回数据时,对数据进行加密。首先定义一个keyconstkey=Buffer.from("6b65796b65796b65796b65796b65796b65796b65796b65796b65796b65796b65796b6579","hex").toString("utf8");然后进行指定的资源行加密,这里只以glb文件为例"./public/models/Xbot.glb");console.log("filepath:",filepath);if(!fs.existsSync(filepath)){res.status(404).send("");}else{constcipher=crypto.createCipheriv("aes-192-ctr",key,Buffer.alloc(16,0));constbuf=Buffer.from(filepath);res.setHeader("内容类型","application/octet-stream");fs.createReadStream(buf).pipe(cipher).pipe(res);//fs.createReadStream(buf).pipe(res);}});加密后模型无法打开,见下图返回加密前说明加密有效。不应打开下载的模型。前端解密加入serviceworker相关代码入口文件。先注册if("serviceWorker"innavigator){navigator.serviceWorker.register("/sw.js").then(function(reg){//注册成功console.log("注册成功.Scopeis"+reg.范围);}).catch(函数(错误or){//注册失败console.log("Registrationfailedwith"+error);});}添加一个sw.ts文件,通过sw抓取文件,对数据进行解密self.addEventListener("install",(event)=>{console.log("installing");});self.addEventListener("activate",(event)=>{console.log("activating");});constfinish=()=>{};constdecrypt=(v)=>{};self.addEventListener("fetch",function(event:any){event.respondWith((asyncfunction(){consturl=event.request.url;if(event.request.url.endsWith(".glb")){constresponse=awaitfetch(event.request);if(response.status!==200)returnresponse;constreader=response.body.getReader();conststream=newReadableStream({start(controller){functionpush(){reader.read().then(({done,value})=>{console.log("value:",va卢);如果(完成){控制器。关闭();完成(网址);返回;}控制器。排队(解密(值,网址));推();});}推();},});返回新的响应(流);}else{returnfetch(event.request);}})();});打开控制台查看是否注册成功。解密代码可以用c/c++或者rust等语言,然后编译成wasm,这里使用rust解密lib.rsexterncratewasm_bindgen;externcrateaes_ctr;externcratehex;#[macro_use]externcratelazy_static;useaes_ctr::Aes192Ctr;使用aes_ctr::stream_cipher::generic_array::GenericArray;使用aes_ctr::stream_cipher::{NewStreamCipher、SyncStreamCipher、SyncStreamCipherSeek};使用std::collections::HashMap;使用std::sync::Mutex;使用wasm_bindgen::prelude::*;lazy_static!{staticrefcipherMap:Mutex
