原生js混淆。

189 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 22 天,点击查看活动详情 响应式数据的基本实现

前端纯原生项目,发布的时候,调试模式打开,很容易直接被别人五分钟,复制一份一模一样网站出来。为了防止被盗窃,假如混淆。

压缩html文件

html-minifier

混淆js文件

javascript-obfuscator

完整代码。


const fs = require('fs');
const path = require('path');
const minify = require('html-minifier').minify;
const JO = require("javascript-obfuscator");

// 项目根目录
let sRootPath = 'src'

fnTravel(sRootPath, fnCompressHtmlCss)
console.log("文件处理结束")

/**
 * 遍历文件
 * @params {*} dir 文件夹
 * @params {*} callback 回调函数
 */
function fnTravel(dir, callback) {
    // 同步读取指定目录的内容
    let aFiles = fs.readdirSync(dir, 'utf8'), aFilterPath = ['.git', '.idea', , 'README'];
    aFiles.forEach((file) => {
        // 获得完整路径
        let pathname = path.join(dir, file)
        // 判断是否处理
        let bIsHandle = aFilterPath.some(item => pathname.includes(item));
        if (!bIsHandle) {
            // 同步获取详细信息
            let stats = fs.statSync(pathname);
            if (stats.isDirectory()) {
                // 文件夹
                fnTravel(pathname, callback)
            } else {
                // 文件
                try {
                    if (pathname.endsWith('js')) {
                        // js 文件
                        fnObfuscatorJs(pathname)
                    } else {
                        // 其他文件
                        callback(pathname)
                    }
                } catch (e) {
                    console.error("压缩失败:" + pathname);
                    console.error(e);
                }
            }
        }
    })
}

/**
 * 压缩 html、css 文件方法
 * @params {*} path 文件路径
 */
function fnCompressHtmlCss(path) {
    let sTargetPath = path.replace('src', 'dist'),
        sCompressData = null, data = null;
    if (!path.includes('img') && !path.includes('fonts')) {
        // 获取文件内容
        data = fs.readFileSync(path, 'utf8')
        if (!path.endsWith('.min.css') && (path.endsWith('.html') || path.endsWith('.css'))) {
            // 压缩后的文件内容
            sCompressData = minify(data, {
                collapseWhitespace: true, // 删除html里的空格 达到html的压缩
                removeAttributeQuotes: true, // 尽可能删除html标签里的双引号 达到html的压缩
                removeComments: true, //删除html中的注释
                removeCommentsFromCDATA: true, //从脚本和样式删除的注释
                minifyCSS: true, // 压缩css
            })
        }
    }
    let result = sCompressData ? sCompressData : data;
    // 将数据同步写入文件
    fnWriteFileRecursive(path, sTargetPath, result, (err) => {
        if (err) console.error(err);
        console.log('compress--[' + path + ']--success');
    })
}

/**
 * 压缩、混淆js方法
 * @params {*} path 文件路径
 */
function fnObfuscatorJs(path) {
    let sTargetPath = path.replace('src', 'dist'),
        sObfuscateCode = null;
    // 获取文件内容
    let data = fs.readFileSync(path, 'utf8')
    if (!sTargetPath.endsWith('.min.js') && !sTargetPath.endsWith('utils.js')) {
        // 进行混淆
        sObfuscateCode = JO.obfuscate(data, {
            compact: true, // 压缩
            controlFlowFlattening: true,
            controlFlowFlatteningThreshold: 1,
            numbersToExpressions: true,
            simplify: true,
            shuffleStringArray: true,
            splitStrings: true,
            stringArrayThreshold: 1,
        });
    }
    // getObfuscatedCode() 返回混淆后的代码字符串,调用 toString 方法也会返回混淆代码
    let sResultCode = sObfuscateCode ? sObfuscateCode.getObfuscatedCode() : data;
    fnWriteFileRecursive(path, sTargetPath, sResultCode, (err) => {
        if (err) console.error(err);
        console.log('compress--[' + path + ']--success');
    })
}

/**
 * 创建目录并写入文件
 * @params {*} sFromPath 来源路径
 * @params {*} sTargetPath 目标路径
 * @params {*} buffer 文件内容
 * @params {*} callback 回调函数
 * */
function fnWriteFileRecursive(sFromPath, sTargetPath, buffer, callback) {
    let lastPath = sTargetPath.substring(0, sTargetPath.lastIndexOf("/"));

    // let lastPath = sTargetPath.substring(0, sTargetPath.lastIndexOf("\\"));



    // 创建目录
    fs.mkdir(lastPath, {
        recursive: true
    }, (err) => {
        if (err) return callback(err);
        if (sTargetPath.endsWith('.js') || sTargetPath.includes('.html') || sTargetPath.includes('.css')) {
            // 处理html、css、js文件
            fs.writeFileSync(sTargetPath, buffer, function (err) {
                if (err) return callback(err);
            });
        } else {
            // 处理图片、字体文件
            let readStream = fs.createReadStream(sFromPath); // 逐块读取文件
            let writeStream = fs.createWriteStream(sTargetPath); // 对指定文件创建一个“可写数据流”
            readStream.pipe(writeStream); // 将文件数据流导向 writeStream 对象
        }
    });
}

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 22 天,点击查看活动详情 响应式数据的基本实现