中文字幕一区二区三区在线中文-日本中文字幕 在线观看-欧美日韩国产亚洲综合-性色AV一二三天美传媒

廣州總部電話:020-85564311
20年
互聯(lián)網(wǎng)應(yīng)用服務(wù)商
廣州總部電話:020-85564311
20年
互聯(lián)網(wǎng)應(yīng)用服務(wù)商
請輸入搜索關(guān)鍵詞
知識庫 知識庫

優(yōu)網(wǎng)知識庫

探索行業(yè)前沿,共享知識寶庫

安全:生產(chǎn)環(huán)境不要在 JS 中暴露后端接口地址,麻煩給寫一個 vite 插件處理下

發(fā)布日期:2025-07-30 08:55:57 瀏覽次數(shù): 810 來源:前端路引
推薦語
前端安全新思路:用Vite插件巧妙隱藏生產(chǎn)環(huán)境API地址,兼顧安全與開發(fā)效率。

核心內(nèi)容:
1. 安全需求背景與前端API地址暴露問題分析
2. 基于異或加密的輕量級解決方案實現(xiàn)
3. Vite插件開發(fā)全流程與自動化替換技巧
小優(yōu) 網(wǎng)站建設(shè)顧問
專業(yè)來源于二十年的積累,用心讓我們做到更好!

脑壳痛,最近莫名其妙的安全抓得严,有安全团队就提了个要求,生产环境下的 JS 中不想看到后端接口地址。

说实话,就算 JS 文件中看不到接口地址,也没办法掩饰前端往后端发送网络请求的事实,只是做了一个掩耳盗铃的效果而已。

既然人家安全专家提出了要求,那就只有改吧改吧了~~

由于项目已经上线有一段时间了,有一大堆的后端 API 接口地址存在,必然不可能一个一个的去修改!!

就只能折腾一下 vite 插件,让 JS 插件在构建时候自动替换接口地址了。

准备加解密方法

第一步,肯定需要一对加解密方法,用来处理字符串的加解密,由于咱只需要掩盖接口地址,而且代码在浏览器端运行,安全性啥的没办法保证,就用一个最简单的异或加密就行了:

123456789101112131415161718192021222324252627282930313233// 简单的 XOR 加密函数function encrypt(str, key) {  let result = '';  for (let i = 0; i < str.length; i++) {    const charCode = str.charCodeAt(i) ^ key.charCodeAt(i % key.length);    // 转换为16进制字符串,确保可打印字符    result += ('00' + charCode.toString(16)).slice(-2);  }  return result;}// 解密函数function decrypt(encrypted, key) {try {    let result = '';    for (let i = 0; i < encrypted.length; i += 2) {      const hex = encrypted.substr(i, 2);      const charCode = parseInt(hex, 16) ^ key.charCodeAt((i/2) % key.length);      result += String.fromCharCode(charCode);    }    return result;  } catch(e) {    console.error('[API Decrypt Error]', e);    return'';  }}/* 测试代码 */var key = 'test-string-xxx'var code = encrypt('/api/login', key)console.log(code);var str = decrypt(code, key)console.log(str)

有了上面两个加解密方法,那就可以开始折腾 vite 插件了~~

vite 插件

这一步一言难尽,各种折腾,具体过长就不摆了,最后有了一个 vite-plugin-api-encrypt.js 插件文件,内容如下:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576// 简单的 XOR 加密函数function encrypt (str, key) {  let result = '';  for (let i = 0; i < str.length; i++) {    const charCode = str.charCodeAt(i) ^ key.charCodeAt(i % key.length);    // 转换为16进制字符串,确保可打印字符    result += ('00' + charCode.toString(16)).slice(-2);  }  return result;}// 生成解密函数function generateDecryptFunction (key, functionName = 'decrypt') {  return `// API路径解密函数const ${functionName} = (encrypted) => {  try {    let result = '';    for (let i = 0; i < encrypted.length; i += 2) {      const hex = encrypted.substr(i, 2);      const charCode = parseInt(hex, 16) ^ '${key}'.charCodeAt((i/2) % ${key.length});      result += String.fromCharCode(charCode);    }    return result;  } catch(e) {    console.error('[API Decrypt Error]', e);    return '';  }};`;}export default function (options = {}) {  const {    key = 'api-encode-key',    decryptName = '__decryptApi' // 全局解密函数名  } = options;  return {    name: 'vite-plugin-api-encrypt',    transform (code, id) {      // 只处理JS/TS文件      if (!/\.(js|ts|vue)$/.test(id)) return;      // 匹配 /api/xxx 格式的字符串      const regex = /['"`](\/api\/[^'"`]+)['"`]/g;      let transformedCode = code;      let match;      while ((match = regex.exec(code)) !== null) {        const [fullMatch, apiPath] = match;        // 加密路径        const encrypted = encrypt(apiPath, key);        // 替换为解密函数调用        transformedCode = transformedCode.replace(          fullMatch,          `${decryptName}('${encrypted}')`        );      }      return transformedCode;    },    transformIndexHtml (html) {      // 注入全局解密函数到HTML头部      return html.replace(        '<head>',        `<head>          <script>            // 运行时解密函数            ${generateDecryptFunction(key, decryptName)}          </script>`      );    }  };}

然后在 vite.config.js 中配置使用插件:

123456789101112131415161718192021222324import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueDevTools from 'vite-plugin-vue-devtools'import apiEncrypt from './vite-plugin-api-encrypt'; // 插件路径// https://vite.dev/config/export default defineConfig({  plugins: [    vue(),    vueDevTools(),    apiEncrypt({      key: 'key', // 自定义加密 key      decryptName: '__decrypt' // 自定义全局函数名    })  ],  resolve: {    alias: {      '@': fileURLToPath(new URL('./src', import.meta.url))    },  },})

启动项目后,效果还不错,会将所有 /api 前缀的字符串都进行加密,再套一个解密方法,就像下面这样:

到这一步已经能解决接口地址暴露问题了,但还是不够完美,解密方法被全局暴露在 index.html 文件中,这也太明显了,有没有办法在需要解密的文件中注入一个解密方法,不要全局暴露??

完善插件

解决问题:在需要的位置插入解密方法,不要在 index.html 中全局暴露解密方法!!

修改 vite-plugin-api-encrypt.js 文件:

123456789101112131415161718192021222324252627282930313233343536373839404142// 其他部分不变,只需要修改暴露的插件对象export default function (options = {}) {  const {    key = 'api-encode-key',    decryptName = '__decryptApi' // 全局解密函数名  } = options;  return {    name: 'vite-plugin-api-encrypt',    transform (code, id) {      // 只处理JS/TS文件      if (!/\.(js|ts|vue)$/.test(id)) return;      // 匹配 /api/xxx 格式的字符串      const regex = /['"`](\/api\/[^'"`]+)['"`]/g;      let transformedCode = code;      let match;      let hasApiPath = false;      while ((match = regex.exec(code)) !== null) {        const [fullMatch, apiPath] = match;        // 加密路径        const encrypted = encrypt(apiPath, key);        // 替换为解密函数调用        transformedCode = transformedCode.replace(          fullMatch,          `${decryptName}('${encrypted}')`        );        hasApiPath = true;      }      // 如果文件中有API路径,注入解密函数      if (hasApiPath) {        const decryptCode = generateDecryptFunction(key, functionName);        transformedCode = `${decryptCode}\n${transformedCode}`;      }      return transformedCode;    },  };}

执行 build 命令构建之后,代码就是这样的:

每次插入的解密方法会造成代码重复,不过影响不大,解密方法就那么几行而已。如果实在有代码洁癖,还是可以考虑将解密方法抽离出来插入到 index.html 入口文件中!!


这里有一个小小的问题:解密方法插入的文字在文件开头,按照 ES 规范来说,文件开头应该是所有的 import 语句,但代码在浏览器运行没报错,这里就没管他了。

就像这样:


優(yōu)網(wǎng)科技,優(yōu)秀企業(yè)首選的互聯(lián)網(wǎng)供應(yīng)服務(wù)商

優(yōu)網(wǎng)科技秉承"專業(yè)團隊、品質(zhì)服務(wù)" 的經(jīng)營理念,誠信務(wù)實的服務(wù)了近萬家客戶,成為眾多世界500強、集團和上市公司的長期合作伙伴!

優(yōu)網(wǎng)科技成立于2001年,擅長網(wǎng)站建設(shè)、網(wǎng)站與各類業(yè)務(wù)系統(tǒng)深度整合,致力于提供完善的企業(yè)互聯(lián)網(wǎng)解決方案。優(yōu)網(wǎng)科技提供PC端網(wǎng)站建設(shè)(品牌展示型、官方門戶型、營銷商務(wù)型、電子商務(wù)型、信息門戶型、微信小程序定制開發(fā)、移動端應(yīng)用(手機站、APP開發(fā))、微信定制開發(fā)(微信官網(wǎng)、微信商城、企業(yè)微信)等一系列互聯(lián)網(wǎng)應(yīng)用服務(wù)。


我要投稿

姓名

文章鏈接

提交即表示你已閱讀并同意《個人信息保護聲明》

專屬顧問 專屬顧問
掃碼咨詢您的優(yōu)網(wǎng)專屬顧問!
專屬顧問
馬上咨詢
聯(lián)系專屬顧問
聯(lián)系專屬顧問
聯(lián)系專屬顧問
掃一掃馬上咨詢
掃一掃馬上咨詢

掃一掃馬上咨詢

和我們在線交談!