详解 dotenv 的使用与实现

news/2024/11/15 21:29:38/文章来源:https://www.cnblogs.com/miniwa/p/18383359

每当涉及到保护API密钥或我们不想因为开源项目而向公众展示的东西时,我们总是倾向于.env文件,而它的解析依赖到dotenv包,一个每周都有31k+开发人员下载的软件包。其设计的理念是Twelve-Factor App的第三点。配置与代码分离。

关于Twelve-Factor App大家可以前往这里查看:https://12factor.net/

为什么文件名只有.env?

文件名只能以.env开头,是一个误解。使用任何名字,它仍然可以在node.js上正常工作。

那为什么要用点开头?

当涉及到环境文件时,在文件名前面使用一个点(.)被认为是好的,因为在任何文件名前面添加一个点都会使其成为一个隐藏的文件或文件夹。

这就是为什么您的操作系统中有多个文件夹,这些文件夹是隐藏的,只能通过CLI访问,例如.ssh、.github、.vscode等。

使用介绍

首先,在你的 Node.js 项目中安装 dotenv

npm install dotenv

在项目的根目录中创建一个名为 .env 的文件。这个文件中每一行定义一个环境变量,格式为 KEY=VALUE。例如:

# .env 文件
PORT=3000
DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3
SECRET_KEY=mysecretkey

注意:.env 文件通常不会提交到版本控制系统(如 Git),因此你可以在 .gitignore 文件中添加一行 /.env 来忽略它。

在你的应用程序入口文件中(通常是 app.jsindex.js),加载并配置 dotenv。这通常是你在应用程序中最早执行的操作之一:

require('dotenv').config();// 现在你可以通过 process.env 访问环境变量
console.log(process.env.PORT); // 输出: 3000
console.log(process.env.DB_HOST); // 输出: localhost

在一些情况下,你可能需要为不同的环境(如开发、测试、生产)使用不同的 .env 文件。你可以通过 dotenv 的配置选项来指定加载的文件:

require('dotenv').config({ path: './config/.env.dev' });

如果想让配置支持使用变量替换,你可以使用 dotenv-expand 来实现:

# .env 文件
HOST=localhost
PORT=3000
FULL_URL=http://${HOST}:${PORT}
const dotenv = require('dotenv');
const dotenvExpand = require('dotenv-expand');const myEnv = dotenv.config();
dotenvExpand.expand(myEnv);console.log(process.env.FULL_URL); // 输出: http://localhost:3000

源码实现

dotenv的源码很简单,只有1个主要文件:https://github.com/motdotla/dotenv/blob/master/lib/main.js

键值解析

核心原理是将 .env 文件解析为键值对,并加载到 process.env 中。在实现上主要是通过使用正则表达式,并处理了字符串、引号、换行符等特殊情况。

这个正则,表示看着头疼。。。

const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg

加解密

在16.1.x版本之后,还增加了解密的功能。可以用于一些安全要求较高的项目中。有了这个可以放心地把.env文件提交到生产了。

function decrypt (encrypted, keyStr) {const key = Buffer.from(keyStr.slice(-64), 'hex')let ciphertext = Buffer.from(encrypted, 'base64')const nonce = ciphertext.subarray(0, 12)const authTag = ciphertext.subarray(-16)ciphertext = ciphertext.subarray(12, -16)try {const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce)aesgcm.setAuthTag(authTag)return `${aesgcm.update(ciphertext)}${aesgcm.final()}`} catch (error) {// 处理解密错误throw error}
}

该函数使用 AES-256-GCM 算法进行解密操作。AES-256 表示它使用 256 位的密钥进行加密,GCM 是一种加密模式,除了加密数据,还可以验证数据的完整性。

  • 加密: 先用 256 位的密钥加密信息,然后生成一个“标签”,这个标签是用来验证数据有没有被改动的。
  • 解密: 先检查标签,如果标签正确,才会用密钥解密数据。如果标签不对,就说明数据可能被篡改了,解密就会失败。

你会不会有疑问,这里是解密,那加密呢?dotenv自身不提供加密的功能,加密依赖于一个工具,dotenvx。https://dotenvx.com/docs/。

dotenvx 是 dotenv 的扩展或增强版,通常基于 dotenv 的功能进行构建,使用时也会依赖于 dotenv 的基础设施。dotenvx 提供了更加专业和复杂的功能,适用于更高要求的应用场景。

另外也可以通过dotenvx ext genexample命令生成一个env的配置例子文件。

灵活的配置入口

dotenv 支持灵活设置配置文件地址,这主要依赖于configDotenv 函数。同时,还内置了调试功能,通过 _debug 函数输出详细的调试信息,帮助开发者快速定位问题。

function configDotenv (options) {const dotenvPath = path.resolve(process.cwd(), '.env')let encoding = 'utf8'const debug = Boolean(options && options.debug)if (options && options.encoding) {encoding = options.encoding}// 加载并解析 .env 文件let optionPaths = [dotenvPath]if (options && options.path) {// 自定义路径处理逻辑}// 解析并填充环境变量let lastErrorconst parsedAll = {}for (const path of optionPaths) {try {const parsed = DotenvModule.parse(fs.readFileSync(path, { encoding }))DotenvModule.populate(parsedAll, parsed, options)} catch (e) {if (debug) {_debug(`Failed to load ${path} ${e.message}`)}lastError = e}}// 填充到 process.envDotenvModule.populate(processEnv, parsedAll, options)return { parsed: parsedAll, error: lastError }
}

总结

关于dotenvx,这里说多一点,真是一个好工具。除了上面介绍的用来加密,也可以用来生成配置用例

dotenvx ext genexample

也可以用来设置环境文件,不用在项目里自己调用dotenv也可以

dotenvx run -f .env.production -- node index.js

最后,提一下注意事项:

  • 非加密的env配置文件,不要提交到代码仓库。除非你确信其中不包含任何敏感信息。
  • 分环境管理:为不同环境创建 .env 文件,例如 .env.development, .env.production
  • 确保 .env 文件的权限设置是适当的,以防止未经授权的访问。

本文由mdnice多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/788170.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3的学习---10

10. Vuex 10.1 Vuex简介 10.1.1 Vuex概述 Vuex 是 Vue.js 应用程序的状态管理模式 + 库。它作为中央存储库,用于管理应用程序中所有组件的状态,并以可预测的方式进行状态变更。Vuex 的设计理念是基于 Flux 架构,主要由以下几个核心概念组成:State(状态):存储应用程序的所…

佛说阿含正行经

闻如是:一时,佛在舍卫国祇树给孤独园。是时,佛告诸比丘:“我为汝说经,上语亦善,中语亦善,下语亦善,语深说度世之道,正心为本。听我言,使后世传行之。”诸比丘叉手受教。佛言:“人身中有五贼,牵人入恶道。何等五贼?一者色,二者痛痒,三者思想,四者生死,五者识,…

如何发明 SAM

如何发明 SAM 我们想做一个结构,接受全部的子串,我们发现,如果考虑增量构造,每次加 1 个字符,增多的字串就是原来的后缀加上这个。 那么我们就这样做。所以我们一直需要一个集合,这些点接受全部后缀。(以上由红线组成的到根的路径就是我们每个时刻维护的集合,即所有后缀…

北京工作居住证申请

登录北京国际人才网 https://www.bjrcgz.gov.cn/ 先择个人登录-...到工作居住证申报页面 关联单位,找公司人事,输入后,关联,等公司审核 材料准备: 需要离职证明与社保记录合成一个PDF上传至其他材料里(离职证明必须与上家社保缴纳主体一致)应税材料:查询日期与社保查…

信息学奥赛初赛天天练-76-NOIP2015普及组-基础题1-计算机存储、硬件系统、操作系统、进制转换、二进制加法

NOIP 2016 普及组 基础题1 1 1MB 等于 ( ) A 10000 字节 B 1024 字节 C 10001000 字节 D 10241024 字节 2 在 PC 机中,PENTIUM(奔腾)、酷睿、赛扬等 是指( ) A 生产厂家名称 B 硬盘的型号 C CPU 的型号 D 显示器的型号 3 操作系统的作用是( ) A 把源程序译…

Pinely Round 4 (Div. 1 + Div. 2) VP记录

Pinely Round 4 (Div. 1 + Div. 2) VP记录 场上打了 ABCDF,被 E 二粉兔创飞了。 这场的构造题有:B D E G I,乐死了。 A 把数列黑白染色,第一个格为黑色,那么每次删除会删除一个黑格子和一个白格子。而黑格子始终比白格子多一个,因此最后选到的是黑格子。答案极为黑格子的…

云知声多模态模型:实时多模态输入输出;独立于 Siri ,苹果或开发新 AI 用于机器人丨 RTE 开发者日报

开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文章 」、「有看点的 会议 」,但内容仅代表编辑…

dolphinscheduler 自定义参数任务传递

select concat(year(CURRENT_DATE())-2,"-01-01 00:00:00") as deleteTime 下一个任务 ${deleteTime} 直接引用

STM32或者RSIC-V输出SPWM波形

直接上代码吧,其余的内容可以到别的地方搜索,包括什么是SPWM/*@Note PWM output routine: TIM1_CH1(PA8)This example demonstrates that the TIM_CH1(PA8) pin outputs PWM in PWM mode 1 andPWM mode 2. */ #include "debug.h" /* PWM Output Mode Definition */…

Fins TCP协议理解及C Sharp实现思路

假设本文中使用到设备的ip地址,用于后续内容的理解: 客户端(本机电脑 windows系统)IP: 192.168.1.101 服务端(PLC omron CJ2M系列)IP 和 端口号 : 192.168.1.10 : 9600注意: ①本文中的 FINS TCP 报文都是以16进制(Hex)发送出去的,所以对应的转换也都会转成16进制的形…