当数据库中的密码存储在 Node.js 中以散列形式存储时,如何使用原始密码登录?
出于安全原因,存储在数据库中的密码始终采用(散列+盐)形式。当用户在任何网站上注册时,都会以原始形式给出密码。但是将原始密码直接存储在数据库中并不是一个好习惯。使用一些服务器端逻辑,我们首先将原始密码转换为(散列+盐)形式,然后将其存储在数据库中。这带来了一个新的挑战,如何比较用户在登录时给出的原始密码,并根据密码是否正确授予用户访问权限。
密码以下列形式存储在数据库中:
Hashed(password, salt)
例子:
22cbca6aa74d3546971ca355a7641649b222a7858f97952f50d68ea65b3c5067a008ea4cdc8974461090a36d7815215
ab4659d2333a94b15d43c758f4d08ab60.7fb85e87188dd649
设置逻辑以使用原始密码登录的步骤:
- 在用户提供的唯一用户名或电子邮件的帮助下搜索数据库以登录。
- 查找唯一记录,如果未找到,则返回“用户不存在”。
- 在 ' 处拆分加密密码。 ' 单独查找散列密码和盐。
- 散列用户提供的原始密码,以使用盐使用 Node.js 'scrypt' 方法登录。
- 将得到的散列值与拆分数据库密码得到的散列值进行比较。
- 如果两个哈希值相等,则用户登录并授予访问权限。
- 如果两者的哈希值不相等,则拒绝访问并显示无效密码消息。
注意:为了说明逻辑,这里我们采用本地或自定义数据库。同样的逻辑也可以用MongoDB、MySql等常规数据库来实现。
示例:此示例说明了当数据库中存储的密码为(散列+盐)形式时如何使用原始密码登录。
javascript
const util = require('util')
const crypto = require('crypto')
const express = require('express')
const bodyParser = require('body-parser')
const repo = require('./repository')
const app = express()
const scrypt = util.promisify(crypto.scrypt)
const port = process.env.PORT || 3000
// The body-parser middleware to parse form data
app.use(bodyParser.urlencoded({ extended: true }))
// Get route to display HTML form to sign in
app.get('/signin', (req, res) => {
res.send(`
`)
})
// Post route to handle form submission logic and
app.post('/signin', async (req, res) => {
// Email and password submitted by the user
const { email, password } = req.body
// Find record by given unique username or email
const user = await repo.findBy({ email })
console.log(user)
// If record not found by given username
if (!user) {
return res.send('User Not Exist')
}
// Hashed and salt of database password
const [hashed, salt] = user.password.split('.')
// Hashing raw password submitted by the user
// to sign in third argument is the key length
// that must be same when hashing the password
// to store it into the database when user sign up
const hashedBuff = await scrypt(password, salt, 64)
console.log(hashed)
console.log(hashedBuff.toString('hex'))
// Compare saved hashed of database and
// obtained hashed
const isValid = hashed === hashedBuff.toString('hex')
if (isValid) {
return res.send('Sign In successfully')
}
return res.send('Invalid Password')
})
// Server setup
app.listen(port, () => {
console.log(`Server start on port ${port}`)
})
javascript
// Importing node.js file system, util,
// crypto module
const fs = require('fs')
const util = require('util')
const crypto = require('crypto')
// Convert callback based scrypt method
// to promise based method
const scrypt = util.promisify(crypto.scrypt)
class Repository {
constructor(filename) {
// The filename where datas are
// going to store
if (!filename) {
throw new Error(
'Filename is required to create a datastore!')
}
this.filename = filename
try {
fs.accessSync(this.filename)
} catch (err) {
// If file not exist it is created
// with empty array
fs.writeFileSync(this.filename, '[]')
}
}
async findBy(attrs) {
// Read all file contents of the datastore
const jsonRecords = await
fs.promises.readFile(this.filename, {
encoding: 'utf8'
})
// Parsing json records in javascript
// object type records
const records = JSON.parse(jsonRecords)
// Iterating through each record
for (let record of records) {
let found = true
// Iterate through each given
// propert for each record
for (let key in attrs) {
// If any given property not matches
// with record record is discarded
if (record[key] !== attrs[key]) {
found = false
}
}
// If 'found' remains true after iterating
// through each given property that
// means record found
if (found) {
return record
}
}
}
}
// The 'datastore.json' file created at runtime
// if it not exist, here we try to fetch
// information from database using some properties
// that means database(datastore.json) already
// exist and there are also records in it.
module.exports = new Repository('datastore.json')
文件名:repository.js该文件包含与创建本地数据库以及如何与之交互相关的所有逻辑。
javascript
// Importing node.js file system, util,
// crypto module
const fs = require('fs')
const util = require('util')
const crypto = require('crypto')
// Convert callback based scrypt method
// to promise based method
const scrypt = util.promisify(crypto.scrypt)
class Repository {
constructor(filename) {
// The filename where datas are
// going to store
if (!filename) {
throw new Error(
'Filename is required to create a datastore!')
}
this.filename = filename
try {
fs.accessSync(this.filename)
} catch (err) {
// If file not exist it is created
// with empty array
fs.writeFileSync(this.filename, '[]')
}
}
async findBy(attrs) {
// Read all file contents of the datastore
const jsonRecords = await
fs.promises.readFile(this.filename, {
encoding: 'utf8'
})
// Parsing json records in javascript
// object type records
const records = JSON.parse(jsonRecords)
// Iterating through each record
for (let record of records) {
let found = true
// Iterate through each given
// propert for each record
for (let key in attrs) {
// If any given property not matches
// with record record is discarded
if (record[key] !== attrs[key]) {
found = false
}
}
// If 'found' remains true after iterating
// through each given property that
// means record found
if (found) {
return record
}
}
}
}
// The 'datastore.json' file created at runtime
// if it not exist, here we try to fetch
// information from database using some properties
// that means database(datastore.json) already
// exist and there are also records in it.
module.exports = new Repository('datastore.json')
使用以下命令运行index.js文件:
node index.js
文件名:package.json
数据库:
输出:
在这里,我们分别使用不同的用户名和密码组合提交三个表单,并分别获得如图所示的输出。
重定向页面: