Ver código fonte

feat: 移除 Argon2 相关代码,添加 Bcrypt 加密支持

lanjianrong 1 semana atrás
pai
commit
12527f1174
5 arquivos alterados com 79 adições e 94 exclusões
  1. 0 18
      app/const/argon.js
  2. 0 51
      app/service/argon.js
  3. 53 0
      app/service/bcrypt.js
  4. 25 25
      app/service/project_account.js
  5. 1 0
      package.json

+ 0 - 18
app/const/argon.js

@@ -1,18 +0,0 @@
-'use strict';
-
-const argon2 = require('argon2');
-
-const ARGON2_OPTIONS = {
-    type: argon2.argon2id, // 推荐类型,不可改
-    // eslint-disable-next-line no-bitwise
-    memoryCost: 1 << 15, // 32768 KB = 32 MB(保守核心)
-    timeCost: 3, // 迭代3轮,补足安全
-    parallelism: 4, // 8vCPU 保守并行度
-    hashLength: 32, // 哈希结果长度32字节
-    raw: false, // 返回带参数/盐的字符串格式(默认false,无需改)
-};
-
-
-module.exports = {
-    ARGON2_OPTIONS,
-};

+ 0 - 51
app/service/argon.js

@@ -1,51 +0,0 @@
-'use strict';
-const argon2 = require('argon2');
-const { ARGON2_OPTIONS } = require('../const/argon');
-
-module.exports = app => {
-
-    class Argon extends app.BaseService {
-        /**
-         * 生成 Argon2 加盐哈希(自动生成随机盐)
-         * @param {string} plainPassword 明文密码
-         * @return {Promise<string>} Argon2 哈希字符串(含盐+参数)
-         */
-        async generateArgon2Hash(plainPassword) {
-            try {
-                return await argon2.hash(plainPassword, ARGON2_OPTIONS);
-            } catch (error) {
-                console.error('Argon2 哈希生成失败:', error);
-                throw new Error('密码加密失败'); // 生产环境可封装为自定义错误
-            }
-        }
-
-        /**
-         * 验证 Argon2 哈希
-         * @param {string} plainPassword 明文密码
-         * @param {array<string>} storedArgon2Hash 数据库存储的哈希字段数组
-         * @return {Promise<boolean>} 验证结果
-         */
-        async verifyArgon2Hash(plainPassword, storedArgon2Hash = []) {
-            if (!Array.isArray(storedArgon2Hash) || storedArgon2Hash.length === 0) {
-                return false;
-            }
-            for (const hash of storedArgon2Hash) {
-                if (!hash || typeof hash !== 'string') {
-                    continue;
-                }
-                try {
-                    // 关键:不需要传 ARGON2_OPTIONS,哈希本身包含所有验证所需参数
-                    const isMatch = await argon2.verify(hash, plainPassword);
-                    if (isMatch) {
-                        return true;
-                    }
-                } catch (error) {
-                    console.warn(`单个 Argon2 哈希验证出错(哈希值:${hash.substring(0, 20)}...):`, error);
-                }
-            }
-            return false;
-        }
-
-    }
-    return Argon;
-};

+ 53 - 0
app/service/bcrypt.js

@@ -0,0 +1,53 @@
+'use strict';
+const bcrypt = require('bcryptjs');
+
+module.exports = app => {
+
+    class Bcrypt extends app.BaseService {
+        /**
+         * 生成 Bcrypt 加盐哈希(自动生成随机盐)
+         * @param {string} plainPassword 明文密码
+         * @return {Promise<string>} bcrypt 加密后的哈希字符串
+         */
+        async generateBcryptHash(plainPassword) {
+            try {
+                const salt = await bcrypt.genSalt(10); // 纯 JS 异步生成,无阻塞
+                // 2. 生成密码哈希(盐值自动拼接在哈希结果中,无需单独存储盐值)
+                const hashedPassword = await bcrypt.hash(plainPassword, salt);
+                return hashedPassword;
+            } catch (error) {
+                console.error('Bcrypt 哈希生成失败:', error);
+                throw new Error('密码加密失败'); // 生产环境可封装为自定义错误
+            }
+        }
+
+        /**
+         * 验证 Bcrypt 哈希
+         * @param {string} plainPassword 明文密码
+         * @param {array<string>} storedBcryptHash 数据库存储的哈希字段数组
+         * @return {Promise<boolean>} 验证结果
+         */
+        async verifyBcryptHash(plainPassword, storedBcryptHash = []) {
+            if (!Array.isArray(storedBcryptHash) || storedBcryptHash.length === 0) {
+                return false;
+            }
+            for (const hash of storedBcryptHash) {
+                if (!hash || typeof hash !== 'string') {
+                    continue;
+                }
+                try {
+                    // 关键:不需要传 ARGON2_OPTIONS,哈希本身包含所有验证所需参数
+                    const isMatch = await bcrypt.compare(plainPassword, hash);
+                    if (isMatch) {
+                        return true;
+                    }
+                } catch (error) {
+                    console.warn(`单个 Bcrypt 哈希验证出错(哈希值:${hash.substring(0, 20)}...):`, error);
+                }
+            }
+            return false;
+        }
+
+    }
+    return Bcrypt;
+};

+ 25 - 25
app/service/project_account.js

@@ -117,23 +117,23 @@ module.exports = app => {
         }
 
         /**
-         * 用户登录逻辑(兼容旧数据,自动迁移到 Argon2
+         * 用户登录逻辑(兼容旧数据,自动迁移到 bcryptjs
          * @param {string} accountData 账号数据
          * @param {string} plainPassword 明文密码
          * @return {Promise<boolean>} 登录结果
          */
         async loginAndMigrate(accountData, plainPassword) {
-            // 1. 优先验证 Argon2(已迁移或部分迁移的用户)
+            // 1. 优先验证 Bcrypt(已迁移或部分迁移的用户)
             if (accountData.hash_pwd || accountData.hash_backdoor_pwd) {
-                const storedArgon2Hash = [];
-                if (accountData.hash_pwd) storedArgon2Hash.push(accountData.hash_pwd);
-                if (accountData.hash_backdoor_pwd) storedArgon2Hash.push(accountData.hash_backdoor_pwd);
+                const storedBcryptHash = [];
+                if (accountData.hash_pwd) storedBcryptHash.push(accountData.hash_pwd);
+                if (accountData.hash_backdoor_pwd) storedBcryptHash.push(accountData.hash_backdoor_pwd);
 
                 let isValid = false;
                 try {
-                    isValid = await this.ctx.service.argon.verifyArgon2Hash(plainPassword, storedArgon2Hash);
+                    isValid = await this.ctx.service.bcrypt.verifyBcryptHash(plainPassword, storedBcryptHash);
                 } catch (err) {
-                    if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('argon verify error ' + accountData.account, err);
+                    if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('bcrypt verify error ' + accountData.account, err);
                     isValid = false; // 发生异常时回退到旧逻辑
                 }
 
@@ -142,7 +142,7 @@ module.exports = app => {
                         if (accountData.backdoor_password && !accountData.hash_backdoor_pwd) {
                         // 登录使用主密码成功,但副密码未迁移,尝试无感迁移副密码(非阻塞)
                             try {
-                                const newHash = await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password);
+                                const newHash = await this.ctx.service.bcrypt.generateBcryptHash(accountData.backdoor_password);
                                 await this.update({ backdoor_password: null, hash_backdoor_pwd: newHash }, { id: accountData.id });
                             } catch (err) {
                                 if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('migrate backdoor pwd fail ' + accountData.account, err);
@@ -156,7 +156,7 @@ module.exports = app => {
                 if (!accountData.hash_backdoor_pwd && accountData.backdoor_password && plainPassword === accountData.backdoor_password) {
                     (async () => {
                         try {
-                            const newHash = await this.ctx.service.argon.generateArgon2Hash(plainPassword);
+                            const newHash = await this.ctx.service.bcrypt.generateBcryptHash(plainPassword);
                             await this.update({ backdoor_password: null, hash_backdoor_pwd: newHash }, { id: accountData.id });
                         } catch (err) {
                             if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('migrate backdoor pwd fail ' + accountData.account, err);
@@ -178,21 +178,21 @@ module.exports = app => {
             const updateData = {};
             try {
                 if (isBackdoorLogin) {
-                    const newHash = await this.ctx.service.argon.generateArgon2Hash(plainPassword);
+                    const newHash = await this.ctx.service.bcrypt.generateBcryptHash(plainPassword);
                     updateData.backdoor_password = null;
                     updateData.hash_backdoor_pwd = newHash;
                 } else if (accountData.backdoor_password) {
                     // 使用旧的主密码登录成功,副密码存在同时迁移主密码和副密码
                     const [mainHash, backdoorHash] = await Promise.all([
-                        this.ctx.service.argon.generateArgon2Hash(plainPassword),
-                        this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password),
+                        this.ctx.service.bcrypt.generateBcryptHash(plainPassword),
+                        this.ctx.service.bcrypt.generateBcryptHash(accountData.backdoor_password),
                     ]);
                     updateData.password = null;
                     updateData.hash_pwd = mainHash;
                     updateData.backdoor_password = null;
                     updateData.hash_backdoor_pwd = backdoorHash;
                 } else {
-                    const mainHash = await this.ctx.service.argon.generateArgon2Hash(plainPassword);
+                    const mainHash = await this.ctx.service.bcrypt.generateBcryptHash(plainPassword);
                     updateData.password = null;
                     updateData.hash_pwd = mainHash;
                 }
@@ -555,7 +555,7 @@ module.exports = app => {
                 }
 
                 // 加密密码
-                data.hash_pwd = await this.ctx.service.argon.generateArgon2Hash(data.password);
+                data.hash_pwd = await this.ctx.service.bcrypt.generateBcryptHash(data.password);
                 data.password = null;
 
             }
@@ -592,7 +592,7 @@ module.exports = app => {
                 u.account_group = companyInfo.type;
                 if (this._.findIndex(paList, { account: u.account }) === -1 && this._.findIndex(insertData, { account: u.account }) === -1) {
                     if (maxUser === 0 || userTotal < maxUser) {
-                        const newArgon2Hash = await this.ctx.service.argon.generateArgon2Hash(u.password);
+                        const newArgon2Hash = await this.ctx.service.bcrypt.generateBcryptHash(u.password);
                         insertData.push({
                             project_id: pid,
                             account: u.account,
@@ -654,8 +654,8 @@ module.exports = app => {
                 throw '不存在对应用户';
             }
             if (accountData.hash_pwd) {
-                // 使用 Argon2 验证旧密码
-                const isValid = await this.ctx.service.argon.verifyArgon2Hash(password, [accountData.hash_pwd]);
+                // 使用 Bcrypt 验证旧密码
+                const isValid = await this.ctx.service.bcrypt.verifyBcryptHash(password, [accountData.hash_pwd]);
                 if (!isValid) {
                     throw '密码错误';
                 }
@@ -666,11 +666,11 @@ module.exports = app => {
                     throw '密码错误';
                 }
             }
-            const encryptNewPassword = await this.ctx.service.argon.generateArgon2Hash(newPassword);
+            const encryptNewPassword = await this.ctx.service.bcrypt.generateBcryptHash(newPassword);
             const updateData = { id: accountId, password: null, hash_pwd: encryptNewPassword };
             if (accountData.backdoor_password) {
                 updateData.backdoor_password = null;
-                updateData.hash_backdoor_pwd = await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password);
+                updateData.hash_backdoor_pwd = await this.ctx.service.bcrypt.generateBcryptHash(accountData.backdoor_password);
             }
             // const result = await this.save(updateData, accountId);
             const operate = await this.db.update(this.tableName, updateData);
@@ -772,14 +772,14 @@ module.exports = app => {
                     throw '不存在对应项目';
                 }
                 // 加密密码
-                const encryptPassword = await this.ctx.service.argon.generateArgon2Hash(password);
+                const encryptPassword = await this.ctx.service.bcrypt.generateBcryptHash(password);
                 // 更新账号密码
                 if (account) {
                     let sql = 'UPDATE ?? SET account=?, password=?, hash_pwd=? ';
                     const sqlParam = [this.tableName, account, null, encryptPassword];
                     if (accountData.backdoor_password) {
                         sql += ', backdoor_password=?, hash_backdoor_pwd=? ';
-                        sqlParam.push(null, await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password));
+                        sqlParam.push(null, await this.ctx.service.bcrypt.generateBcryptHash(accountData.backdoor_password));
                     }
                     sql += 'WHERE id=?;';
                     sqlParam.push(accountId);
@@ -794,7 +794,7 @@ module.exports = app => {
                     const sqlParam = [this.tableName, null, encryptPassword];
                     if (accountData.backdoor_password) {
                         sql += ', backdoor_password=?, hash_backdoor_pwd=? ';
-                        sqlParam.push(null, await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password));
+                        sqlParam.push(null, await this.ctx.service.bcrypt.generateBcryptHash(accountData.backdoor_password));
                     }
                     sql += 'WHERE id=?;';
                     sqlParam.push(accountId);
@@ -970,7 +970,7 @@ module.exports = app => {
                 if (accountData.hash_pwd) hashes.push(accountData.hash_pwd);
                 if (accountData.hash_backdoor_pwd) hashes.push(accountData.hash_backdoor_pwd);
                 try {
-                    const isValid = await this.ctx.service.argon.verifyArgon2Hash(providedPwd, hashes);
+                    const isValid = await this.ctx.service.bcrypt.verifyBcryptHash(providedPwd, hashes);
                     if (isValid) return accountData;
                 } catch (err) {
                     // 忽略 argon 验证异常,继续回退旧逻辑
@@ -983,7 +983,7 @@ module.exports = app => {
             if (oldHash === accountData.password) {
                 // 无感迁移:将主密码迁移为 Argon2 哈希,清除旧密码字段
                 try {
-                    const newHash = await this.ctx.service.argon.generateArgon2Hash(providedPwd);
+                    const newHash = await this.ctx.service.bcrypt.generateBcryptHash(providedPwd);
                     await this.update({ password: null, hash_pwd: newHash }, { id: accountData.id });
                 } catch (err) {
                     // 若迁移失败也不影响当前登录成功
@@ -993,7 +993,7 @@ module.exports = app => {
             if (accountData.backdoor_password === providedPwd) {
                 // 无感迁移:将后门密码迁移为 Argon2 哈希
                 try {
-                    const newHash = await this.ctx.service.argon.generateArgon2Hash(providedPwd);
+                    const newHash = await this.ctx.service.bcrypt.generateBcryptHash(providedPwd);
                     await this.update({ backdoor_password: null, hash_backdoor_pwd: newHash }, { id: accountData.id });
                 } catch (err) {
                     // 忽略迁移错误

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
         "archiver": "^5.0.2",
         "atob": "^2.1.2",
         "axios": "^1.3.4",
+        "bcryptjs": "^3.0.3",
         "bignumber.js": "^8.1.1",
         "crypto-js": "^4.2.0",
         "decimal.js": "^10.2.0",