在具有用户身份验证功能的实际应用程序中,将用户密码作为原始字符串存储在数据库中是不切实际的,但将密码散列然后将它们存储到数据库中是一种很好的做法。
Node JS 的加密模块帮助开发人员散列用户密码。
例子:
Original Password : portalforgeeks
Hashed Password : bbf13ae4db87d475ca0ee5f97e397248a23509fc10c82f
1e3cf110b352c3ca6cc057955ace9d541573929cd7a74a
280a02e8cb549136b43df7704caaa555b38a
使用 Crypto 模块进行密码散列
为了演示 Crypto 模块的使用,我们可以创建一个简单的登录和注册 API 并使用 Postman 对其进行测试。
我们将使用两个函数:
- cryto.randomBytes(“length”) :生成给定“长度”的加密强数据。
- crypto.pbkdf2Sync(“password”, “salt”, “iterations”, “length”, “digest”) :用“salt”散列“password”,迭代次数等于给定的“iterations”(更多的迭代意味着更安全的密钥) 并使用“摘要”中给出的算法并生成长度等于给定“长度”的密钥。
项目依赖:
- 节点 JS:用于后端服务器。
- 用于创建服务器的 express 模块。
- 用于 MongoDB 连接和查询的 mongoose 模块。
- 用于散列的加密模块。
- 用于解析 json 数据的 body-parser。
执行操作的步骤
- 首先创建如下目录结构:
hashApp --model ----user.js --route ----user.js --server.js
- 创建定义用户架构的模型/user.js 文件
// Importing modules const mongoose = require('mongoose'); var crypto = require('crypto'); // Creating user schema const UserSchema = mongoose.Schema({ name : { type : String, required : true }, email : { type : String, required : true }, hash : String, salt : String }); // Method to set salt and hash the password for a user // setPassword method first creates a salt unique for every user // then it hashes the salt with user password and creates a hash // this hash is stored in the database as user password UserSchema.methods.setPassword = function(password) { // Creating a unique salt for a particular user this.salt = crypto.randomBytes(16).toString('hex'); // Hashing user's salt and password with 1000 iterations, 64 length and sha512 digest this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, `sha512`).toString(`hex`); }; // Method to check the entered password is correct or not // valid password method checks whether the user // password is correct or not // It takes the user password from the request // and salt from user database entry // It then hashes user password and salt // then checks if this generated hash is equal // to user's hash in the database or not // If the user's hash is equal to generated hash // then the password is correct otherwise not UserSchema.methods.validPassword = function(password) { var .hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, `sha512`).toString(`hex`); return this.hash === hash; }; // Exporting module to allow it to be imported in other files const User = module.exports = mongoose.model('User', UserSchema);
- 创建路由/user.js 文件:
// Importing modules const express = require('express'); const router = express.Router(); // Importing User Schema const User = require('../model/user'); // User login api router.post('/login', (req, res) => { // Find user with requested email User.findOne({ email : req.body.email }, function(err, user) { if (user === null) { return res.status(400).send({ message : "User not found." }); } else { if (user.validPassword(req.body.password)) { return res.status(201).send({ message : "User Logged In", }) } else { return res.status(400).send({ message : "Wrong Password" }); } } }); }); // User signup api router.post('/signup', (req, res, next) => { // Creating empty user object let newUser = new User(); // Initialize newUser object with request data newUser.name = req.body.name, newUser.email = req.body.email // Call setPassword function to hash password newUser.setPassword(req.body.password); // Save newUser object to database newUser.save((err, User) => { if (err) { return res.status(400).send({ message : "Failed to add user." }); } else { return res.status(201).send({ message : "User added successfully." }); } }); }); // Export module to allow it to be imported in other files module.exports = router;
- 创建 server.js 文件:
// Importing modules var express = require('express'); var mongoose = require('mongoose'); var bodyparser = require('body-parser'); // Initialize express app var app = express(); // Mongodb connection url var MONGODB_URI = "mongodb://localhost:27017/hashAppDb"; // Connect to MongoDB mongoose.connect(MONGODB_URI); mongoose.connection.on('connected', () => { console.log('Connected to MongoDB @ 27017'); }); // Using bodyparser to parse json data app.use(bodyparser.json()); // Importing routes const user = require('./route/user'); // Use user route when url matches /api/user/ app.use('/api/user', user); // Creating server const port = 3000; app.listen(port, () => { console.log("Server running at port: " + port); });
- 使用命令 node server.js 从 hashApp 目录运行 server.js 文件
- 打开 Postman 并创建一个 post 请求到 localhost:3000/api/user/signup 如下:
您将得到如下响应:
用户数据存储在数据库中,如下所示:
{ "_id": { "$oid": "5ab71ef2afb6db0148052f6f" }, "name": "geeksforgeeks", "email": "geek@geeksforgeeks.org", "salt": "ddee18ef6a6804fbb919b25f790005e3", "hash": "bbf13ae4db87d475ca0ee5f97e397248a23509fc10c82f1e3cf110 b352c3ca6cc057955ace9d541573929cd7a74a280a02e8cb549136b43df7704caaa555b38a", "__v": 0 }
- 从 Postman 创建一个 post 请求到 localhost:3000/api/user/login 如下:
您将得到如下响应:
应用:
- 实际应用需要散列密码。
- 加密模块使散列易于实现。
- 散列密码可确保用户的隐私。
参考:
- https://nodejs.org/api/crypto.html
- https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_digest_callback