最近的项目使用express来写,这也是我尝试写的第一个nodejs的大型项目。今天实现了半天的登陆逻辑,还是很费劲的,在此记录一下。
大部分的知识都是从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配合的逻辑。
基本逻辑:
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/side中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 的各位,在你们的源码里学到了很多很多的东西!