bcrypt算法简介
- bcrypt算法相对来说是运算比较慢的算法,在密码学界有句常话:越慢的算法越安全。算法越算,黑客破解成本越高.通过salt和const这两个值来减缓加密过程,ta的加密时间(百ms级)远远超过md5(大概1ms左右)。对于计算机来说,Bcrypt 的计算速度很慢,但是对于用户来说,这个过程不算慢。bcrypt是单向的,而且经过salt和cost的处理,使其受rainbow攻击破解的概率大大降低,同时破解的难度也提升不少,相对于MD5等加密方式更加安全,而且使用也比较简单.
- bcrypt加密后的字符串形如: 2a 10 asdjflkaydgigadfahgl.asdfaoygoqhgasldhf,其中: 是分割符,无意义;2a是bcrypt加密版本号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了;这里的const值即下面代码中的saltRounds,生成salt的迭代次数,默认值是10,推荐值12。
使用npm bcrypt模块的两种方式:
sync同步写法(同步写起来简单,没太大影响)
//引入bcrypt模块
var bcrypt = require('bcrypt');
//POST /signup用户注册
router.post('/', checkNotLogin, function(req, res, next){
let password = req.fields.password //获取注册页面上表单输入的密码
//生成salt的迭代次数
const saltRounds = 10;
//随机生成salt
const salt = bcrypt.genSaltSync(saltRounds);
//获取hash值
var hash = bcrypt.hashSync(password, salt);
//把hash值赋值给password变量
password = hash;
storeUInfo();
//存储用户信息
function storeUInfo(){
let user = {
name: name,
password: password, //把加密后的密码存入数据库
gender: gender,
avatar: avatar,
bio: bio
}
//用户信息写入数据库
UserModel.create(user)
.then(function(result){
//此时user是插入mongodb后的值,包含_id
user = result.ops[0];
//删除密码这种敏感信息,将用户信息存入session
delete user.password
//注册成功后跳转到首页
res.redirect('/posts')
})
.catch(function(e){
//用户名被占用则跳回注册页,而不是错误页
if(e.message.match('duplicate key')){
req.flash('error','用户名已被占用')
return res.redirect('/signup')
}
next(e)
})
}
//POST /signin 用户登录
router.post('/', checkNotLogin, function(req, res, next){
const password = req.fields.password //获取登录页面上表单输入的密码
UserModel.getUserByName(name)
.then(function(user){
//检查数据库里的密码和用户输入的密码是否匹配,user.password为数据库里存储的密码
const pwdMatchFlag =bcrypt.compareSync(password, user.password);
if(pwdMatchFlag){
...
res.redirect('/posts') //匹配成功跳转到主页
}else{
...
return res.redirect('back') //匹配失败返回之前的页面
}
})
.catch(next)
})
async异步写法(稍微麻烦些,推荐)
//引入bcrypt模块
var bcrypt = require('bcrypt');
//POST /signup用户注册
router.post('/', checkNotLogin, function(req, res, next){
let password = req.fields.password //获取注册页面上表单输入的密码
//生成salt的迭代次数
const saltRounds = 10;
//生成salt并获取hash值
bcrypt.genSalt(saltRounds, function(err, salt){
bcrypt.hash(password,salt, function(err, hash){
//把hash值赋值给password变量
password = hash;
storeUInfo();
})
})
//存储用户信息
function storeUInfo(){
let user = {
name: name,
password: password, //把加密后的密码存入数据库
gender: gender,
avatar: avatar,
bio: bio
}
//用户信息写入数据库
UserModel.create(user)
.then(function(result){
//此时user是插入mongodb后的值,包含_id
user = result.ops[0];
//删除密码这种敏感信息,将用户信息存入session
delete user.password
//注册成功后跳转到首页
res.redirect('/posts')
})
.catch(function(e){
//用户名被占用则跳回注册页,而不是错误页
if(e.message.match('duplicate key')){
req.flash('error','用户名已被占用')
return res.redirect('/signup')
}
next(e)
})
}
//POST /signin 用户登录
router.post('/', checkNotLogin, function(req, res, next){
const password = req.fields.password //获取登录页面上表单输入的密码
UserModel.getUserByName(name)
.then(function(user){
//检查数据库里的密码和用户输入的密码是否匹配,user.password为数据库里存储的密码
bcrypt.compare(password, user.password,function(err,res){
const pwdMatchFlag = res;
tryLogin(pwdMatchFlag);
})
// 尝试登录
function tryLogin(pwdMatchFlag){
if(pwdMatchFlag){
...
res.redirect('/posts') //匹配成功跳转到主页
}else{
...
return res.redirect('back') //匹配失败返回之前的页面
}
}
})
.catch(next)
})
为什么建议使用异步模式?
如果你在一个简单的脚本上使用bcrypt,使用同步模式是非常好的。
但是,如果您在服务器上使用bcrypt,则建议使用异步模式。
这是因为bcrypt完成的散列是CPU密集型的,所以同步版本将阻止事件循环,并阻止你的应用程序服务于任何其他入站请求或事件。