Я уже искал решения в Stack Overflow и Google, но ни одно из решений не работает, я всегда получаю следующую ошибку:
Error: Module not found: Error: Can't resolve 'crypto' in ....
Я уже сделал:
npm update
ng update
auth.service.ts: (не компонент html — не в браузере — скрытая служба аутентификации)
import { Injectable } from '@angular/core';
import { scrypt, randomBytes, timingSafeEqual } from "crypto";
const keyLength = 32;
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor() {};
async login( username: string, password: string ) {
if ( await AuthService.compare(username, await AuthService.hash(password))) {
localStorage.setItem('STATE', 'true');
return true;
}
return false;
}
async logout() {
localStorage.setItem('STATE', 'false');
return true;
}
isLoggedIn() {
const loggedIn = localStorage.getItem('STATE');
if (loggedIn == 'true')
return true;
else
return false;
}
/**
* Has a password or a secret with a password hashing algorithm (scrypt)
* @param {string} password
* @returns {string} The salt+hash
*/
static async hash( password: string ): Promise<string> {
return new Promise((resolve, reject) => {
// generate random 16 bytes long salt - recommended by NodeJS Docs
const salt = randomBytes(16).toString("hex");
scrypt(password, salt, keyLength, (err, derivedKey) => {
if (err) reject(err);
// derivedKey is of type Buffer
resolve(`${salt}.${derivedKey.toString("hex")}`);
});
});
};
/**
* Compare a plain text password with a salt+hash password
* @param {string} password The plain text password
* @param {string} hash The hash+salt to check against
* @returns {boolean}
*/
static async compare( password: string, hash: string ): Promise<boolean> {
return new Promise((resolve, reject) => {
const [salt, hashKey] = hash.split(".");
// we need to pass buffer values to timingSafeEqual
const hashKeyBuff = Buffer.from(hashKey, "hex");
scrypt(password, salt, keyLength, (err, derivedKey) => {
if (err) reject(err);
// compare the new supplied password with the hashed password using timeSafeEqual
resolve(timingSafeEqual(hashKeyBuff, derivedKey));
});
});
};
}
Модуль шифрования встроен в Node.js 22.4.1, который я использую для своего Angular 18: https://nodejs.org/api/crypto.html#cryptoscryptpassword-salt-keylen-options-callback
Решение не в следующем:
npm install crypto
(старый способ)Кроме того, узел, начиная с версии 15, поддерживает API веб-криптографии. nodejs.org/api/crypto.html#cryptowebcrypto
Вам нужно будет использовать что-то доступное в браузерах, например Web Crypto API.
Хеширование может выглядеть примерно так:
static async hash(password: string): Promise<string> {
const salt = crypto.getRandomValues(new Uint8Array(16))
const key = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
{ name: "PBKDF2" },
false,
["deriveBits"])
const derivedBits = await crypto.subtle.deriveBits({
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256"
}, key, 256)
return `${this.bufferToHex(salt)}.${this.bufferToHex(derivedBits)}`
}
Сравнение может выглядеть примерно так:
static async compare(password: string, hash: string): Promise<boolean> {
const [saltHex, storedHashHex] = hash.split('.')
const key = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
{ name: "PBKDF2" },
false,
["deriveBits"]
)
const derivedBits = await crypto.subtle.deriveBits({
name: "PBKDF2",
salt: this.hexToBuffer(saltHex),
iterations: 100000,
hash: "SHA-256"
}, key, 256)
return storedHashHex === this.bufferToHex(derivedBits)
}
Для того, чтобы это сработало, не нужно import
ничего делать. Я не эксперт по безопасности, поэтому не могу гарантировать безопасность этих алгоритмов, но PBKDF2 обычно считается приемлемым для хеширования паролей.
Этот фрагмент кода выполняется на стороне сервера? Вероятно, нет, в браузере у вас нет чего-то вроде криптомодуля, но у вас есть Developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API, который также доступен в Node и может быть полезен. выбор.