В системах, использующих ORM Prisma, работа с пользователями почти
всегда требует организации безопасного хранения паролей. Прямое
сохранение пароля в базе данных недопустимо, поэтому применяется
одностороннее хеширование. В JavaScript-экосистеме одной из наиболее
распространённых библиотек для этой задачи является
bcrypt.js.
bcrypt.js реализует алгоритм bcrypt, который специально
разработан для хранения паролей с учётом высокой стоимости вычислений и
встроенной поддержки соли. Это делает перебор паролей существенно более
дорогим с точки зрения вычислительных ресурсов.
Для начала работы достаточно установить пакет:
npm install bcryptjs
Библиотека является чисто JavaScript-реализацией, что обеспечивает её совместимость с большинством сред выполнения Node.js без необходимости компиляции нативных модулей.
bcrypt использует концепцию адаптивного хеширования:
Типичная форма хеша:
$2a$10$............................................
Где:
2a — версия алгоритма10 — количество раундов (стоимость вычисления)Prisma чаще всего используется через сервисный слой, который инкапсулирует бизнес-логику. Именно в этом слое корректно размещать хеширование паролей, а не в контроллерах и не в Prisma schema middleware без необходимости.
/src
/services
user.service.ts
/repositories
user.repository.ts
/prisma
client.ts
Сервисный слой отвечает за преобразование входных данных перед сохранением в базу.
import bcrypt fr om 'bcryptjs';
import { prisma } fr om '../prisma/client';
const SALT_ROUNDS = 10;
export async function createUser(data) {
const hashedPassword = await bcrypt.hash(data.password, SALT_ROUNDS);
return prisma.user.create({
data: {
email: data.email,
password: hashedPassword,
},
});
}
bcrypt.hash выполняет генерацию соли и хешированиеSALT_ROUNDSДля аутентификации используется сравнение входного пароля с сохранённым хешем.
import bcrypt fr om 'bcryptjs';
import { prisma } from '../prisma/client';
export async function validateUser(email, password) {
const user = await prisma.user.findUnique({
wh ere: { email },
});
if (!user) {
return null;
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return null;
}
return user;
}
bcrypt.compare не расшифровывает хешПараметр cost factor напрямую влияет на безопасность и производительность.
Типичные значения:
const SALT_ROUNDS = 12;
Рост значения увеличивает время хеширования экспоненциально.
Размещение bcrypt-логики в сервисах решает несколько архитектурных задач:
// антипаттерн
prisma.user.create({
data: {
password: data.password, // небезопасно
},
});
Перенос логики в контроллер усложняет повторное использование и тестирование.
При обновлении пользователя необходимо проверять, изменён ли пароль.
if (data.password) {
data.password = await bcrypt.hash(data.password, SALT_ROUNDS);
}
При частичном обновлении данных важно не перезаписывать хеш без необходимости.
export async function updateUser(id, data) {
const updateData = { ...data };
if (data.password) {
updateData.password = await bcrypt.hash(data.password, SALT_ROUNDS);
}
return prisma.user.update({
wh ere: { id },
data: updateData,
});
}
bcrypt является вычислительно затратным алгоритмом, поэтому при высокой нагрузке важно учитывать:
В системах с высокой нагрузкой иногда рассматриваются альтернативы (argon2), однако bcrypt остаётся широко используемым стандартом.
Корректная архитектура включает:
Дополнительно важно:
prisma.user.findUnique({
wh ere: { email },
select: {
id: true,
email: true,
},
});
Все операции bcrypt являются асинхронными, что критично для масштабируемых Node.js приложений.
Использование синхронных методов:
bcrypt.hashSync(password, SALT_ROUNDS);
в сервисных слоях нежелательно из-за блокировки event loop.
Общая последовательность обработки данных:
Такая структура обеспечивает разделение ответственности и предсказуемость поведения системы.