如何直接调用passport.serializeUser(并避免完全调用身份验证)?

时间:2014-04-24 15:13:55

标签: passport.js

问题:

简而言之 - 如何直接调用护照serializeUser函数?我对我提供的注册实现感到满意,我只需要能够在没有调用身份验证的间接的情况下调用它来执行此操作。

passport.serializeUser(function (user, done) {
    if (user) {
        done(null, user);
    }
});

作为一名已经过身份验证的用户,在Controller中,我希望能够执行以下操作:

var user = req.user;   // get the already authenticated user out of the session
user.roles = ['agency']
passport.serializeUser(user);   // persist my changes in session storage

deserializeUser将知道如何处理更改。

背景:

使用Passport进行有效的MEAN Stack应用程序,以便使用本地策略进行身份验证。一切都很好 - 会话管理外部化为Redis。但是,应用程序中要求能够“登录”另一类用户到系统。管理员(具有管理员角色的用户)可以例如以其中一个用户身份登录。实际上,我希望这是一种幻觉 - 我所要做的就是改变一些关于管理员的设置(角色信息等,并在会话中存储 - 更改的数据存储在堆栈中(js数组 - 纪念模式概念) - 当管理员进行用户注销时,我将关于管理员的原始信息弹回到他的会话数据中。

所以我真正想要的是能够调整一些有关已经登录的管理员用户的用户详细信息,并使用这些更改(序列化它们)更新其会话信息,而无需重新进行身份验证(除了我没有的任何其他内容)用于调用身份验证的未加密密码,因此不是一个选项)。管理员已经过身份验证。 - 在deserializeUser上,逻辑将检查丰富的数据,如果存在,则在反序列化数据时做正确的事情,以留下管理员是假定的用户身份的印象。

更新

好的,所以看起来行为与我原先预期的非常不同。如下:

1)。 passport.serializeUser似乎只在身份验证(登录)时被调用。 passport.deserializeUser会在每个请求上被调用,并且通常会多次调用 - 因此,如果在那里放置数据库查询,则每个请求可能会有多个查询。

2)。 req.user是passport.deserializeUser更新的对象。这是来自req._passport.session.user的DISTINCT !!他们不一样......

3)。 req._passport.session.user直接更新会话存储。因此,如果您更改req._passport.session.user指向的内容或更改其属性,它们将在内部更新会话存储 - 我可以通过查看Redis DB中的会话状态来查看此行为。

要解决上面的问题,我实际上并不需要直接调用passport.serializeUser。我只需要使用我希望持久存储到会话存储的属性信息来更新req._passport.session.user。但这里有一个关键点 - 我相信可以更新的属性必须与passport.serializeUser最初用于序列化初始状态时已经存在的属性相匹配。最后一点需要确认。

所以我可以更新req._passport.session.user,只要它遵守已存储在会话中的属性,它就会持久存储到会话存储中。然后我可以覆盖passport.deserializeUser的行为,以使用作为参数传入的内容。同样,这里有一个令人困惑的地方 - 作为passport.deserializeUser的用户参数传递的是req._passport.session.user对象,passport.deserializeUser最终返回的是稍后可访问的req.user对象。

2 个答案:

答案 0 :(得分:0)

https://github.com/jaredhanson/passport/blob/master/lib/http/request.js

function here....
...
var self = this;
    this._passport.instance.serializeUser(user, this, function(err, obj) {
      if (err) { self[property] = null; return done(err); }
      self._passport.session.user = obj;
      done();
    });

答案 1 :(得分:0)

好的,所以Passport在这里非常灵活,可以满足我的需要。只需首先了解内部实施方式。在这里,我将称之为解决方案" Parasitic Session Polymorphism"因为它使用现有会话的方式,但在运行时注入足够的新信息似乎是不同的用户类型等。所以解决方案基本上是(如果有人希望使用基本想法,需要换掉自定义代码):

passport.serializeUser(function (user, done) {
    if (user) {
        var identityStack = user.identityStack || [];
        var parentId = user.parentId || '';
        var userStorage = {
            _id: user._id,
            identityStack: identityStack,
            entityId: user.entityId,
            entityType: user.entityType,
            roles: user.roles,
            parentId: parentId
        };
        done(null, userStorage);
    }
});


passport.deserializeUser(function (userStorage, done) {
    var id = userStorage._id;
    User.findOne({_id: id}).exec(function (err, user) {
        if (user) {
            if(userStorage.identityStack && userStorage.identityStack.length > 0) {
                user.identityStack = userStorage.identityStack;
                user.entityId = userStorage.entityId;
                user.entityType = userStorage.entityType;
                user.roles = userStorage.roles;
                user.parentId = userStorage.parentId
            }
            return done(null, user);
        } else {
            return done(null, false);
        }
    });
});

然后,为了从您自己的代码中实际调用passport.serializeUser,您可以通过currying http req对象来实现所需的结果。对于我的实现,我从与.prototype相关联的javascript数组中推送并弹出添加的信息,作为Mongoose用户模型的扩展:

按键代码:

            var memento = {
                entityId : agency.id,
                entityType: 'user',
                roles: ['admin', 'user']
            };
            var user = req.user;
            user.pushIdentity(memento);
            var doneWrapper = function (req) {
                var done = function (err, user) {
                    if(err) {
                        console.log('Error occurred serializing user');
                        return;
                    }
                    req._passport.session.user = user;
                    console.log('user serialized successfully');
                    return;
                };
                return done;
            };
            req._passport.instance.serializeUser(user, doneWrapper(req));
            return res.send({success: true, user: req.user});

和POP密钥代码:

 var user = req.user;
    user.popIdentity();
    var doneWrapper = function (req) {
        var done = function (err, user) {
            if(err) {
                console.log('Error occurred serializing user');
                return;
            }
            req._passport.session.user = user;
            console.log('user serialized successfully');
            return;
        };
        return done;
    };
    req._passport.instance.serializeUser(user, doneWrapper(req));
    return res.send({success: true, user: user});

显然,代码尚未准备就绪,将console.log语句转移到日志记录策略等等,但这是基本概念。 serializeUser方法的签名在lib / authenticator.js下的Passport github代码库中定义。

关键部分:

 Authenticator.prototype.serializeUser = function(fn, req, done) {
 if (typeof fn === 'function') {
    return this._serializers.push(fn);
 }

// private implementation that traverses the chain of serializers, attempting
// to serialize a user
var user = fn;

// For backwards compatibility
if (typeof req === 'function') {
  done = req;
   = undefined;
}

感谢Biba的贡献,非常感谢并协助我们找到解决方案。