精华 Express+Nodejs 下的登录拦截实现
发布于 2年前 作者 duyinghua 10036 次浏览

原来一直不知道怎么在Express+Nodejs里面添加类似于Struts2的拦截器(因为要使用登录拦截的功能)。 以前一直以为在router这块添加类似一下的转移路由控制权代码的(每个都加很麻烦)

app.get('/show', controllers.checkLogin);//登录验证
app.get('/show', controllers.showList);//实际跳转

又或者是像某些项目里面给每个controller的方法里面加以下这种验证(太繁琐了)

if (!req.session.user) {
        return res.redirect("/login");
    }

最近翻到之前使用session的代码片段,突然有如茅塞顿开啊!!

//session
app.use(function (req, res, next) {
    var err = req.flash('error');
    var success = req.flash('success');
    res.locals({
        user:req.session.user,
        navSide:req.session.navSide,
        error:err.length ? err : null,
        success:success.length ? success : null
    });
    next();
});

这尼玛不就是个拦截器吗~囧~ 稍加改造

//登录拦截器
app.use(function (req, res, next) {
    var url = req.originalUrl;
    if (url != "/login" && !req.session.user) {
        return res.redirect("/login");
    }
    next();
});

经过测试,成功了

23 回复

不错,我是用aop实现的

我用的是route的middleware实现的,你这个其实是实现了express的middleware,和session/bodyparser是一个级别的. 我的实现代码:

//set the authIdentity method for all the handlers.
//catch the exceptions for all the handlers.
var m = ['get','post','put','delete'];
m.forEach(function(type){
  var handlers = app.routes[type];
  if(handlers){
    handlers.forEach(function(handler){
      //TODO:need to config the excluded handler path,not the hard code. 
      if(handler.path !== '/' && handler.path !== '/auth'){//the '/auth' is the login url.
        handler.callbacks = [authIdentity].concat(handler.callbacks);
      }
      handler.callbacks.forEach(function(callback){
        var packageCallBack = function(req,res,next){
          try{callback(req,res,next);}
          catch(e){
            debugger;
            //TODO:need to implement other logics.  
            if(e instanceof AppError){
              res.send(e);
            }
            next(e);
          }
        };
        callback = packageCallBack;
      }); 
    }); 
  }
});

@duyinghua 我使用你这个方式在用登录拦截的时候,我放在了。app.use(express.session()); app.use(app.router);

之间,这样拦截是成功了,。但是静态的资源不能读取了。

静态资源app.use(express.static(path.join(__dirname, 'public')));放在app.use(express.session());后面登录拦截的前面,我的没问题

不懂struts, 但一般就middleware就好了。

@duyinghua 感谢你的回复,拦截是成功了,但是新的问题出现了,session的写入始终是失败的, 我的文件配置app.js如下

`app.configure(function(){ app.set('port’, process.env.PORT || 3000); app.set('views’, __dirname + ‘/views’); app.set('view engine’, ‘ejs’); //app.use(express.logger()); app.use(express.static(path.join(__dirname, ‘public’))); app.use(express.favicon()); app.use(express.logger(‘dev’)); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.cookieParser(“keyboard cat”)); app.use(express.session()); app.use(function(req,res,next){ var url = req.originalUrl; if(url != “/login” && !req.session.user){ return res.redirect(“/login”); // return; } next(); }) app.use(app.router);

}); ` session的写入是在另外一个route里面,具体实现方式是:

`exports.to_login = function(req, res){ var user_name = req.body.user_name; var password = req.body.password; console.log(user_name); console.log(password); var user = { username:user_name, password:password } req.session.user = user; res.render("index",{user:req.session.user}); }

`

这样之后,每次点击登陆之后都会又被拦截到登陆页面

var filter = require('./lib/filter');

判断是否已经登录如果登录了则过去,否则跳转到登录页

exports.authorize = function(req, res, next) {
  if (!req.session.user_id) {
    res.redirect('/admin/login');
  } else {
    next();
  }
}

使用session

app.use(express.cookieParser('sctalk admin manager'));
app.use(express.session());

路由控制,在需要登录验证的路由上加上filter.authorize

app.get('/admin/login',admin.login);
app.get('/admin/logout',admin.logout);

app.get('/admin/:action',filter.authorize, function(req, res, next){
    if(admin[req.params.action])
    {
      admin[req.params.action](req, res, next);
    }
    else
    {
      res.status(404);
      res.end();
    }
});

最后在登录判断时候添加session即可

exports.dologin = function(req, res,next){
    // 校验
    req.assert('username', "用户名不能为空").notEmpty();
    req.assert('password', "密码不能为空").notEmpty();
    var errors = req.validationErrors();
    if(errors && errors.length>0)
    {
      var ermsg = [];
      for(var i=0;i<errors.length;i++)
      {
        ermsg.push(errors[i].msg);
      }
      var json={title:'管理后台-- 请先登录',error:ermsg.join("\n")};
      res.render('admin/login', json);
      return;
    }
    var userid = req.body.username;
    var pwd = req.body.password;
    var ip = req.ip;
    userbiz.checkUser(userid,pwd,ip,function(err,user){
      if(!!err){
        var json={title:'管理后台-- 请先登录',error:err};
        res.render('admin/login', json);
      }
      else{
        req.session.user_id = user.user_id;
        req.session.user = user;
        res.redirect("/admin/index");
      }
     
    });
   
};

倘若有N个路由都需要验证登录或者权限 那每个还都得添加一下filter 岂不是也很不方便

@duyinghua 你可以表达式 一个设置满足多个路由规则的呀。

@duyinghua ‘/admin/:action’ 是个通配符看到哇?

@ronin session的内容不应该是res.render("index",{user:req.session.user});这么传递的,你仔细看我帖子中第三段代码片段,另外下次发帖时希望搞好所贴代码的展示格式。session搞好后你登陆的route部分直接重定向到index就可以了,都不用传user参数

@xuanye okok 不错,话说这个组件就是nodejs的过滤器吗

学习 正好想怎么弄登录呢

@duyinghua 不是啊,自己写的

楼主看下这个https://github.com/tldrio/express-group-handlers 我最近也在做这个东西,搜到了这个。

@ronin session不能写入解决了吗,我也遇到了

没登陆的时候 TypeError: Cannot read property ‘user_id’ of undefined 求解决

我是用Nodejs做server,前台和后台都用oauth2,通过access_token做登陆拦截

@mypayne session 没有启用吗?

这个确实不错。。。 aop了

思路大体差不多啊 实现有些前台地址不需要登陆的验证 login.js

 var needLogin = function(path){
    var noLoginPath = ['/','/ajax']; //不需要登陆的地址

    for(var i =0; i< noLoginPath.length;i++)
    {
        var item = noLoginPath[i];
        if(path == item || (item + '/') == path){
            return false; //不需要登陆
        }
    }

    return true;
}
module.exports = needLogin;

app.js

app.use(function(req,res,next){
    var path = req.originalUrl;
    var needLogin = require('./libs/login');
    var err       = null;
    if(needLogin(path))
    {
        var login = req.session.islogin;
        if(!login){
            err = new Error('Not Login');
            err.status = 500;
        }
    }
    next(err);
})
回到顶部