笔者是一名应届前端技术小白,由于公司的项目需求有幸当一回全栈工程师。这篇文章是对管理后台登录功能的拆解教程,部分参考了前辈的博客,写下这篇文章权当是学习笔记和经验积累。欢迎学习交流,喷子绕道。
http协议属于无状态协议,无法记录上一次的会话信息。但实际业务场景中,我们的需求往往是一次登录成功以后对管理员进行记忆,下次登录时无须再输入用户名和密码。为了保证安全性,存储进入数据库的密码应当是加密过后的哈希值。
当客户端通过表单post提交账号密码过来的时候,后台获取到http请求中body的密码,通过加密算法后进行哈希值比对来验证登录成功与否。
cnpm i express-session --save
cnpm i bcryptjs
操作Mongodb时采用的是mongoose,你应当在user.js里面配置数据库集合的约束条件,采用导出的model来操作数据库。
var mongoose = require('mongoose');
// 定义集合约束条件
var Schema = mongoose.Schema;
var userRuleSchema = new Schema({
"userName": { type: String, required: true, index: { unique: true } },
"password": { type: String, required: true }
});
敏感数据存储进入数据库时必须先对其加密,采用bcrypt来加密。以下代码来自于前端乱炖站长的博客: 传送门
var bcrypt = require('bcryptjs');
var SALT_WORK_FACTOR = 10;
userRuleSchema.pre('save', function (next) {
var user = this;
if (!user.isModified('password')) return next(); //产生密码hash当密码有更改的时候(或者是新密码)
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// 结合salt产生新的hash
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// 使用hash覆盖明文密码
user.password = hash;
next();
});
});
})
链接数据库来测试我们存储进入mongodb的密码是否为hash值
// db.js
var users = require('./models/user');
var mongoose = require('mongoose');
mongoose.connect('mongoodb://127.0.0.1:27017/fashion');
mongoose.connection.on('connected',function(){
console.log('MongoDB connected success');
})
var testUser = new users({
userName:'wuquan',
password:'haha123'
});
testUser.save(function(err){
if(!err){
console.log('存储成功');
}
})
如果按照以上代码来的话,此时在mongodb上面的密码应当是加密的hash值了。我们还应当在user.js里面写一个方法来进行密码哈希值比对
// 密码比对方法
userRuleSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('user',userRuleSchema);
万事俱备,此时只要接受来自于客户端的用户名和密码,查询数据库进行比对就可以了。
var express = require('express');
var router = express.Router();
var users = require('../models/user');
router.post('/login', function (req, res, next) {
var userName = req.body.userName;
var password = req.body.pass;
users.findOne({ userName: userName }, function (err, user) { // 查询数据库
if (err) {
res.json({
status: '0',
msg: '用户名不存在',
result: ''
})
} else {
user.comparePassword(password, function (err, isMatch) {
if (err) {
res.json({
status: '0',
msg: err.message,
result: ''
})
}
if ((password, isMatch) == true) { // 用户名和密码正确
res.json({
status: '1',
msg: '登录成功',
result: ''
});
// 此处还应当写入session
} else {
res.json({
status: '0',
msg: '用户名或者密码错误',
result: ''
})
}
})
}
});
以上是管理后台的账号密码登录校验过程,当登录成功时,我们还应当写入一个session字段返回给客户端,以便于下次客户端进行访问时可以直接跳过登录。
// app.js
var express = require('express');
var app = express();
var session = require('express-session');
app.use(
session({
secret:'secret', // 用来对session_id相关的cookie进行签名
resave:false,
saveUninitialized: false,
cookie: {userName:"default",maxAge: 7*24*60*60*1000} // 设置有效期
})
)
登录成功时写入session
// 当用户名和密码都正确时
req.session.userName = userName;
req.session.isLogin = true;
有些页面我们允许无需登录就可以访问,有些页面我们需要拦截并强制要求其登录。由于项目采用前后端分离,重定向部分交由前端的vue-router来实现。
app.use(function (req, res, next) { // 下次登录时验证session
if (req.session.userName) {
next();
} else { // 未登陆时拦截部分页面请求
if (req.originalUrl == '/fashion') { // 需要将路由拦截部分写在路由规则前面,否则会先跳转到对应的页面去了
res.json({
status: '0',
msg: '当前尚未登录',
result: ''
})
} else {
next();
}
}
});
参考博客链接:
基于Mongoose和bcrypt的密码验证
Nodejs使用session、cookie进行登录验证功能的实现