Passport js 序列化用户和反序列化用户

Posted

技术标签:

【中文标题】Passport js 序列化用户和反序列化用户【英文标题】:Passport js serializeUser & deserializeUser 【发布时间】:2018-03-30 03:54:31 【问题描述】:

我已阅读有关论点的所有内容,但仍然无法理解。 Passport Js 网站上的文档非常模糊。

我正在使用 Passport JS 和 passport-ldapauth 策略。 我没有数据库我显然不想在每个请求上都访问 LDAP 服务器。 我想在 POST /login 路由上使用 LDAP 的护照策略第一次对用户进行身份验证,将用户存储在会话和每个后续请求我只想检查用户是否已经登录。

我正在尝试使用会话,但我无法理解如何将 Passport + 会话与序列化/反序列化流程一起使用。 我检查的每个示例都在 deserializeUser 函数中使用 User.findOne。

截至目前,我禁用了 Passport 会话,我正在使用自定义中间件检查 req.session.user != null。如果是这种情况,用户已经登录,我点击next()。否则重定向到登录。

这是一些代码(为简单起见,我删除了与问题无关的代码):

护照配置

var express = require('express'),
    session = require('express-session'),
    passport = require('passport'),
    LdapStrategy = require('passport-ldapauth');

var app = express();

var LdapStrategyOptions = 
  server: 
    url: '<url>',
    bindDN: '<dn>',
    bindCredentials: "<pwd>",
    searchBase: '<searchBase>',
    searchFilter: '<filter>'
  
;
passport.use(new LdapStrategy(LdapStrategyOptions));

app.use(cookieParser());
app.use(session(
  store: new LokiStore(autosave: false),
  resave: false,
  saveUninitialized: false
  secret: env.get("SESSION_SECRET")
));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded( extended: false ));

app.use(passport.initialize());

路线

// LOGIN ROUTE
app.get('/login',
  function(req, res) 
    res.render('login');
);

// LOGIN HANDLER ROUTE
app.post('/login',
  passport.authenticate('ldapauth',  session: false ),
  function(req, res) 
    req.session.userId = req.user.cn;
    req.session.user = 
      "userId": req.user.cn,
      "displayName": req.user.displayName
    ;
  res.redirect('/');
);

// LOGOUT ROUTE
app.get('/logout',
  function(req, res) 
    req.session.destroy(function(err) 
      req.logout();
      res.redirect('/');
    );
);

// HOME ROUTE
app.get('/', isLoggedIn, function(req, res) 
   res.render('home');
);

IsLoggedIn 中间件

var isLoggedIn = function(req, res, next) 
  if (req.session.user != null)
    console.log("is auth ok '" + req.session.user.userId +"'");
    return next();
  

  console.log("redirect to auth/login");
  res.redirect('/auth/login');

我错过了什么?我的设置中是否存在任何安全问题? 任何帮助表示赞赏。

【问题讨论】:

你有没有可能错过了app.use(passport.session()); 没有。我从示例代码中删除了它,因为正如我写的那样,我没有使用它,因为我不明白我应该在 passport.deserializeUser() 中写什么。示例 cose 正是我发现使其与自定义会话管理一起使用的方式。 【参考方案1】:

来自passportjs docs:

在典型的 Web 应用程序中,用于验证用户身份的凭据只会在登录请求期间传输。如果身份验证成功,将通过用户浏览器中设置的 cookie 建立和维护会话。

每个后续请求将不包含凭据,而是包含标识会话的唯一 cookie。为了支持登录会话,Passport 将对会话中的用户实例进行序列化和反序列化。

基本上serializeUser 应该返回一个唯一的用户标识符,以便您稍后可以将deserializeUser 重新转换为 JSON。

因此,对于您的实现,您可能应该按照以下方式做一些事情:

免责声明:我没有使用 LDAP 的经验。

passport.serializeUser(function(user, done) 
  //We can identify the user uniquely by the CN,
  //so we only serialize this into the session token.
  done(null, user.cn);
);

passport.deserializeUser(function(cn, done) 
  //Directly query LDAP.
  //I'm not sure passport caches the result (only calls deserializeUser for new sessions)
  //but worst case you can cache the result yourself.
  somehowLoadUserFromLDAPByCN(cn, function(err, user) 
    done(err, 
     userId: user.cn,
     displayName: user.displayName
    );
  );
);

【讨论】:

完全正确!但问题是我不想在每个请求上都使用 LDAP(资源密集型进程)。数据库也是如此。为什么我要对每个请求都查询数据库?这正是我在护照​​框架中所不理解的。 我不记得护照是否处理此函数结果的缓存。您可以通过设置断点并查看是否在您已通过身份验证时调用它来检查自己。我的假设是它没有(因为护照不知道您是否要缓存这些结果。您可以自己在此函数中缓存结果,并且只有在缓存未命中时,才点击 LDAP 查询)。缓存的实现取决于您(内存中、redis 等...) deserializeUser 在每个请求上都会被调用,即使你已经通过了身份验证。我仍然无法理解此功能的目的。为什么我应该在每个请求上点击身份验证系统(DB、LDAP、facebook 服务)?如果我可以使用会话来存储用户名、电子邮件和那些东西,我为什么要在数据库中缓存用户配置文件? 因为有时您的数据库包含有关该用户的附加信息(例如角色)【参考方案2】:

如果您只需要一个 id 和一个显示名称,将它们保持在会话中是完全可以的。只有在需要更多字段时才应加载完整的用户配置文件。

【讨论】:

以上是关于Passport js 序列化用户和反序列化用户的主要内容,如果未能解决你的问题,请参考以下文章

Passport-local mongoose node.js 支持多种用户类型

我是不是实现了 NodesJS + Passport + RedisStore 的序列化和反序列化?

当护照js中的序列化和反序列化调用时

Passport.js 异步与同步反序列化

序列化和反序列化来自用户定义类的对象

反序列化为啥要找公开方法