您现在的位置是:网站首页> NodeJS

NodeJs 源码保护

  • NodeJS
  • 2022-08-11
  • 877人已阅读
摘要

现在 NodeJs 开发 Server 端越来越流行,如果 Server 部署在自己公司的服务器上,那么可以认为环境是相对安全的,不需要做源码保护。但是如果需要在客户方部署,又不希望自己的源码暴露的时候,这个时候就需要源码保护。一般的源码保护方式就是 js 压缩/混淆之类的操作,增加 js 代码的不可读性,或者说是增加破解难度。


        本文讨论另一种使用字节码编译 nodejs 代码来保护源码的方式。


        NodeJs 使用 google 的 V8 引擎进行编译,具体可以参考  https://zhuanlan.zhihu.com/p/28590489,同时,我们利用 bytenode 这个插件来辅助生成字节码文件,具体请参考 https://github.com/OsamaAbbas/bytenode。


        项目构成:使用 express 创建一个项目,项目目录如下:


        1.png


        主要文件及说明:


        bin/www:程序主入口


        routes/:路由 js 文件


        services/:核心业务逻辑处理的 js 文件


        app.js:NodeJs Server 启动入口。


        compile.js:字节码编译 js 的文件,后面会讲到。


        其余的文件不重要,也不会被编译成字节码。


        核心思路就是将关键的 js 代码编译成字节码,以保护我们的业务处理逻辑或者算法。


1. 安装依赖


npm install bytenode --save


2. compile.js,主要逻辑就是将项目代码拷贝到 dist 目录中,遍历 dist 下 routes 和 services 等核心 js 文件目录,使用 bytenode 插件将所有的 js 转换成 jsc 字节码文件,然后删除 js 源文件。


var bytenode = require('bytenode');

var fs = require('fs');

var path = require("path");


fs.exists('./dist', exist => {

    if (exist) {

        delDir('./dist');

    }

    fs.mkdirSync('./dist');

})


// 拷贝目录到 dist 下

fs.readdir('./', (err, files) => {

    if (err) {

        console.error(err);

        return;

    }

    for (var i = 0; i < files.length; i++) {

        var stat = fs.statSync('./' + files[i]);

        if (stat.isFile()) {

            if (files[i].indexOf('compile.js') == -1) {

                fs.writeFileSync('./dist/' + files[i], fs.readFileSync('./' + files[i]));

            }

        } else if (stat.isDirectory() && files[i].indexOf('dist') == -1) {

            createDocs('./' + files[i], './dist/' + files[i], function () {


            })

        } else {


        }

    }


    compileFile()

})


function compileFile() {

    // 编译 app.js 为字节码

    bytenode.compileFile({

        filename: './dist/app.js'

    });

    fs.unlinkSync('./dist/app.js');


    // 编译 filters/routes/services 目录下的js文件为字节码

    compileDir('./dist/filters');

    compileDir('./dist/routes');

    compileDir('./dist/services');

}


function compileDir(dir) {

    var stat = fs.statSync(dir);

    if (stat.isFile() && dir.indexOf('.js') != -1) {

        // 文件,直接转换

        bytenode.compileFile({

            filename: dir

        });

        fs.unlinkSync(dir);

    } else if (stat.isDirectory()) {

        // 目录,列出文件列表,循环处理

        var files = fs.readdirSync(dir);

        for (var i = 0; i < files.length; i++) {

            var file = dir + '/' + files[i];

            compileDir(file);

        }

    } else {


    }

}


//递归创建目录 同步方法  

function mkdirsSync(dirname) {

    if (fs.existsSync(dirname)) {

        return true;

    } else {

        if (mkdirsSync(path.dirname(dirname))) {

            console.log("mkdirsSync = " + dirname);

            fs.mkdirSync(dirname);

            return true;

        }

    }

}


function _copy(src, dist) {

    var paths = fs.readdirSync(src)

    paths.forEach(function (p) {

        var _src = src + '/' + p;

        var _dist = dist + '/' + p;

        var stat = fs.statSync(_src)

        if (stat.isFile()) {// 判断是文件还是目录

            fs.writeFileSync(_dist, fs.readFileSync(_src));

        } else if (stat.isDirectory()) {

            copyDir(_src, _dist)// 当是目录是,递归复制

        }

    })

}


/*

 * 复制目录、子目录,及其中的文件

 * @param src {String} 要复制的目录

 * @param dist {String} 复制到目标目录

 */

function copyDir(src, dist) {

    var b = fs.existsSync(dist)

    console.log("dist = " + dist)

    if (!b) {

        console.log("mk dist = ", dist)

        mkdirsSync(dist);//创建目录

    }

    console.log("_copy start")

    _copy(src, dist);

}


function createDocs(src, dist, callback) {

    console.log("createDocs...")

    copyDir(src, dist);

    console.log("copyDir finish exec callback")

    if (callback) {

        callback();

    }

}


function delDir(path) {

    let files = [];

    if (fs.existsSync(path)) {

        files = fs.readdirSync(path);

        files.forEach((file, index) => {

            let curPath = path + "/" + file;

            if (fs.statSync(curPath).isDirectory()) {

                delDir(curPath); //递归删除文件夹

            } else {

                fs.unlinkSync(curPath); //删除文件

            }

        });

        fs.rmdirSync(path);

    }

}


3. 修改 bin/www 文件,在最开始 引入 bytenode


require('bytenode');

var app = require('../app');

var debug = require('debug')('esreader-server:server');

var http = require('http');


...


4. 执行指令打包编译。


node compile.js

编译完成之后, dist 下面的所有文件即可作为发布到第三方服务器上的server。


这样做完之后,项目的启动,或者使用诸如 pm2 等工具来管理 server 时,都与之前的固有做法一致,不需要特殊处理。


上一篇:实用代码下载

下一篇:npm命令行使用

Top