Nodejs 我研究nodeclub 登陆验证逻辑的一些心得
Nodejs 我研究nodeclub 登陆验证逻辑的一些心得
最近的项目使用express来写,这也是我尝试写的第一个nodejs的大型项目。今天实现了半天的登陆逻辑,还是很费劲的,在此记录一下。
因为本人都是靠自己理解然后参照nodeclub的代码写的,所以会有很多问题,希望可以帮忙指出~
大部分的知识都是从https://github.com/cnodejs/nodeclub 这份源码学到的,还有express官方的一个登陆验证示例 https://github.com/visionmedia/express/blob/master/examples/auth/app.js 当然还有api http://expressjs.jser.us/api.html#res.status 但是吐槽一下,express的api。。真心崎岖。。
首先最坑的一点。。。让session生效
app.use(express.cookieParser());//开启cookie
app.use(express.session({//开启session
secret: config.session_secret
}));
app.use(app.router);
那么注意了,前两行一定要写在app.router这个中间件前面!否则session始终未定义! 之后我们开始书写session与cookie配合的逻辑。
基本逻辑: 1.进入网站后(此时没有session),从cookie中取加密的数据,进行验证,若验证成功,在session中存储user,用户免登陆。 2.进入首页时(需要登录的页面),若session中没有user,则自动跳到登陆页面。 3.登陆时,采用post方式提交用户信息,利用加密算法进行加密,存储登录信息到cookie中。 4.数据库采用的mysql,采用啥都无所谓。。因为我是通过http与后端写好的统一登陆接口进行通信的。登陆成功返回0,失败返回-9999
ok,下面来一条条的实现这些逻辑。 首先我们利用crypto这个牛逼的加密加密库进行加密解密操作
var crypto = require('crypto');
//加密
function encrypt(str, secret) {
var cipher = crypto.createCipher('aes192', secret);
var enc = cipher.update(str, 'utf8', 'hex');
enc += cipher.final('hex');
return enc;
}
//解密
function decrypt(str, secret) {
var decipher = crypto.createDecipher('aes192', secret);
var dec = decipher.update(str, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
}
他的作用主要是把用户名密码进行加密存到cookie,读取时解密。而密文我们在配置文件中配置即可。
第一个逻辑需要一个加密与解密的操作。而进入网站开始就进行操作的话,可以使用app.use中间件。
app.use(express.cookieParser());//开启cookie
app.use(express.session({//开启session
secret: config.session_secret
}));
app.use(require(’./controller/site’).auth_user); // 添加这个中间件,用来解析cookie
app.use(app.router);
然后controller/site中auth_user如下
exports.auth_user = function (req, res, next) {
if (req.session.user) {
return next();//若没有session,直接跳过此中间件
} else {
var cookie = req.cookies[config.auth_cookie_name];//读cookie,通过配置文件中标识符读cookie
if (!cookie) {
return next();//若没有此站点的cookie,直接跳过此中间件
}
var auth_token = decrypt(cookie, config.session_secret);//解密操作
var auth = auth_token.split('\t');
var user = auth[0], passwd = auth[1];//解密后拿到username与password
var data = {
parament:user,
password:passwd,
route:'checkLogin',
}
var userLoginConf = config.apiService.userLogin;
postData.send(data, userLoginConf, function (result) {//这部分为post的api请求进行验证。
if (result == 0) {
req.session.user = user.username;//存在此用户,开启session,存储user
return next();//进行下一步
} else {//不存在此用户,进行下一步
return next();
}
})
}
};
第二个逻辑很简单。我们直接使用一个私有函数即可。
/*session无效验证*/
function noSession(req,res){
if (!req.session || !req.session.user) {
res.redirect('login');
return;
}
}
第三个逻辑,配合router进行表单提交的工作
router:
app.post('/login', site.login);
controller:
exports.login = function (req, res) {
/*处理空值与注入*/
var param = handleParam(req.body)
var data ={
parament : param.username,
password: param.password,
route: 'checkLogin',
}
var userLoginConf = config.apiService.userLogin;
postData.send(data,userLoginConf,function(result){//与后端api接口通信
if(result ==0){//登陆成功
var user = {
'username':data.parament,
'password':data.password
}
gen_session(user, res);//生成cookie
/*这个逻辑暂时没开,这是为了做那种“返回刚才页面”的需求
//check at some page just jump to home page
var refer = req.session._loginReferer || 'home';
for (var i = 0, len = notJump.length; i !== len; ++i) {
if (refer.indexOf(notJump[i]) >= 0) {
refer = 'home';
break;
}
}
res.redirect(refer);
*/
req.session.regenerate(function(){//写session。存入username
// Store the user's primary key
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user.username;
res.render('home',[]);//渲染数据,随便怎么渲染了。。
});
}else{
res.render('login',[]);//登录失败,跳转到login。这里我没有加提示信息。
}
})
};
这里处理注入使用了validator模块
var check = require('validator').check,
sanitize = require('validator').sanitize;
/*处理空值与注入*/
function handleParam(params){
var params = params || {};
var safeParam = {};
for(var key in params){
var trimed = sanitize(params[key]).xss();
var blockXssed = sanitize(trimed).xss();
safeParam[key] = blockXssed
}
return safeParam
}
当然最后我们也要有个登出逻辑了
exports.logout = function (req, res, next) {//代码很简单,就不解释了
req.session.destroy();
res.clearCookie(config.auth_cookie_name, { path: '/' });
res.redirect(req.headers.referer || 'login');
};
最后感谢写nodeclub https://github.com/cnodejs/nodeclub 的各位,在你们的源码里学到了很多很多的东西!
Nodejs 我研究nodeclub 登陆验证逻辑的一些心得
最近的项目使用Express来写,这也是我尝试写的第一个Node.js的大型项目。今天实现了半天的登陆逻辑,还是很费劲的,在此记录一下。
因为本人都是靠自己理解然后参照nodeclub的代码写的,所以会有很多问题,希望可以帮忙指出~
大部分的知识都是从https://github.com/cnodejs/nodeclub 这份源码学到的,还有Express官方的一个登陆验证示例 https://github.com/visionmedia/express/blob/master/examples/auth/app.js。当然还有API http://expressjs.jser.us/api.html#res.status 但是吐槽一下,Express的API真心崎岖。
首先最坑的一点是让session生效:
app.use(express.cookieParser()); // 开启cookie
app.use(express.session({ // 开启session
secret: config.session_secret
}));
app.use(app.router);
注意了,前两行一定要写在app.router
这个中间件前面!否则session始终未定义!
之后我们开始书写session与cookie配合的逻辑。
基本逻辑:
- 进入网站后(此时没有session):从cookie中取加密的数据,进行验证,若验证成功,在session中存储user,用户免登陆。
- 进入首页时(需要登录的页面):若session中没有user,则自动跳到登陆页面。
- 登陆时:采用POST方式提交用户信息,利用加密算法进行加密,存储登录信息到cookie中。
- 数据库采用的MySQL:采用什么数据库都无所谓,因为我是通过HTTP与后端写好的统一登陆接口进行通信的。登陆成功返回0,失败返回-9999。
加密与解密操作
首先我们需要一个加密与解密的操作。而进入网站开始就进行操作的话,可以使用app.use
中间件。
app.use(express.cookieParser()); // 开启cookie
app.use(express.session({ // 开启session
secret: config.session_secret
}));
app.use(require('./controller/site').auth_user); // 添加这个中间件,用来解析cookie
app.use(app.router);
然后在controller/site
中添加auth_user
中间件:
exports.auth_user = function (req, res, next) {
if (req.session.user) {
return next(); // 若没有session,直接跳过此中间件
} else {
var cookie = req.cookies[config.auth_cookie_name]; // 读cookie,通过配置文件中标识符读cookie
if (!cookie) {
return next(); // 若没有此站点的cookie,直接跳过此中间件
}
var auth_token = decrypt(cookie, config.session_secret); // 解密操作
var auth = auth_token.split('\t');
var user = auth[0], passwd = auth[1]; // 解密后拿到username与password
var data = {
parament: user,
password: passwd,
route: 'checkLogin',
};
var userLoginConf = config.apiService.userLogin;
postData.send(data, userLoginConf, function (result) { // 这部分为post的api请求进行验证。
if (result == 0) {
req.session.user = user.username; // 存在此用户,开启session,存储user
return next(); // 进行下一步
} else { // 不存在此用户,进行下一步
return next();
}
});
}
};
session无效验证
/* session无效验证 */
function noSession(req, res) {
if (!req.session || !req.session.user) {
res.redirect('login');
return;
}
}
登录逻辑
配合路由进行表单提交的工作:
app.post('/login', site.login);
控制器中的登录逻辑:
exports.login = function (req, res) {
/* 处理空值与注入 */
var param = handleParam(req.body);
var data = {
parament: param.username,
password: param.password,
route: 'checkLogin',
};
var userLoginConf = config.apiService.userLogin;
postData.send(data, userLoginConf, function (result) { // 与后端api接口通信
if (result == 0) { // 登陆成功
var user = {
'username': data.parament,
'password': data.password
};
gen_session(user, res); // 生成cookie
req.session.regenerate(function () { // 写session。存入username
req.session.user = user.username;
res.render('home', []); // 渲染数据,随便怎么渲染了
});
} else {
res.render('login', []); // 登录失败,跳转到login。这里我没有加提示信息。
}
});
};
function handleParam(params) {
var params = params || {};
var safeParam = {};
for (var key in params) {
var trimed = sanitize(params[key]).xss();
var blockXssed = sanitize(trimed).xss();
safeParam[key] = blockXssed;
}
return safeParam;
}
登出逻辑
exports.logout = function (req, res, next) { // 代码很简单,就不解释了
req.session.destroy();
res.clearCookie(config.auth_cookie_name, { path: '/' });
res.redirect(req.headers.referer || 'login');
};
最后感谢写nodeclub https://github.com/cnodejs/nodeclub 的各位,在你们的源码里学到了很多很多的东西!
如果是用express默认的session只能单进程共享,多个实例会有问题的,一般我觉得有cookie就可以了, 为了防止别人直接修改cookie, 我一般给cookie加密并加个摘要,同时cookie name一般md5一下,避免太直接
可以使用nginx的sticky sessions来解决多实例问题
非常感谢楼主的分享~超赞的~
在这个帖子中,作者分享了关于Node.js项目中实现登录验证逻辑的经验。下面是根据原文内容总结的关键部分以及对应的示例代码:
关键点总结
-
启用Session和Cookie:
- 必须确保
cookieParser
和session
中间件在app.router
之前加载。
- 必须确保
-
解密与加密:
- 使用
crypto
模块进行加密和解密操作,以安全地存储用户信息在Cookie中。
- 使用
-
登录验证中间件:
- 在访问需要登录的页面时,检查Session中的用户信息,如果没有则重定向到登录页面。
-
处理登录表单:
- 接收POST请求中的登录数据,并发送到API接口进行验证。
- 如果验证成功,创建新的Session并重定向到主页。
-
登出逻辑:
- 清除Session和Cookie,并重定向到登录页面。
示例代码
启用Session和Cookie
const express = require('express');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const app = express();
app.use(cookieParser());
app.use(session({
secret: 'your_secret_key',
resave: false,
saveUninitialized: true
}));
解密与加密
const crypto = require('crypto');
function encrypt(str, secret) {
let cipher = crypto.createCipher('aes192', secret);
let enc = cipher.update(str, 'utf8', 'hex');
enc += cipher.final('hex');
return enc;
}
function decrypt(str, secret) {
let decipher = crypto.createDecipher('aes192', secret);
let dec = decipher.update(str, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
}
登录验证中间件
exports.auth_user = function (req, res, next) {
if (req.session.user) {
return next();
} else {
var cookie = req.cookies['auth_cookie'];
if (!cookie) {
return next();
}
var auth_token = decrypt(cookie, 'your_secret_key');
var auth = auth_token.split('\t');
var user = auth[0], passwd = auth[1];
// 假设这里是调用后端API进行验证
if (true) {
req.session.user = user;
return next();
} else {
return next();
}
}
};
处理登录表单
exports.login = function (req, res) {
var param = handleParam(req.body);
// 假设这里是调用后端API进行验证
if (true) {
req.session.user = param.username;
res.redirect('/home');
} else {
res.render('login', {});
}
};
function handleParam(params) {
for (let key in params) {
params[key] = sanitize(params[key]).xss();
}
return params;
}
登出逻辑
exports.logout = function (req, res) {
req.session.destroy();
res.clearCookie('auth_cookie', { path: '/' });
res.redirect('/login');
};
以上是基于原文内容整理的示例代码,实际开发中还需要根据具体需求进行调整。