【问题标题】:Do I implement serialize and deserialize NodesJS + Passport + RedisStore?我是否实现了 NodesJS + Passport + RedisStore 的序列化和反序列化?
【发布时间】:2013-10-16 14:55:49
【问题描述】:

我是否实现了序列化和反序列化?

RedisStore 设置为我与 Express 的会话存储。这是否意味着我不实现序列化和反序列化?它会自动发生吗?

当我不实施这些方法时,我会收到以下 Express 错误 - 500 错误:无法将用户序列化到会话中。当我实现它们时,我不确定在反序列化中放入什么。

下面的代码似乎可以工作,但会话没有持续。每次访问该网站时,我都需要登录。

有没有 NodeJS + Passport + RedisStore 的好例子?

var sessionStore = new RedisStore({
                                        host: rtg.hostname,
                                        port: rtg.port,
                                        db: redisAuth[0],
                                        pass: redisAuth[1]
                                      });

passport.use(new ForceDotComStrategy({
    clientID: clientId,
    clientSecret: clientSecret,
    callbackURL: myurl
},
function(token, tokenSecret, profile, done) {
    console.log(profile);
    return done(null, profile);
  }
));

appSecure.configure('production', function(){
appSecure.use(allowCrossDomain);
appSecure.use(express.cookieParser(expressSecret));
appSecure.use(express.bodyParser());
appSecure.use(express.methodOverride());
appSecure.set('port', port); 
appSecure.use(express.session({ secret: expressSecret, store: sessionStore, key:'expressSid', cookie: { maxAge : 604800, domain:'.domain.com'}})); 
appSecure.use(passport.initialize());
appSecure.use(passport.session());
appSecure.use(appSecure.router);
appSecure.use(express.static(__dirname + '/public'));
appSecure.use(express.errorHandler());
});

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

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

【问题讨论】:

    标签: node.js express redis passport.js


    【解决方案1】:

    如果您使用 sessions,您必须为护照提供序列化和反序列化功能。将 Redis 实现为会话存储与护照的实现方式无关,它只处理会话数据的存储位置。

    使用护照实现会话

    正如我所说,必须向护照提供序列化和反序列化功能才能使会话正常工作。

    serialize 函数的目的是返回足够的识别信息,以便在任何后续请求中恢复用户帐户。具体done()方法的第二个参数是序列化到会话数据中的信息

    您提供的反序列化函数旨在根据序列化到会话的识别信息返回用户配置文件

    这是讨论会话部分中Passport Guide 的示例:

    passport.serializeUser(function(user, done) {
      done(null, user.id);
    });
    
    passport.deserializeUser(function(id, done) {
      User.findById(id, function(err, user) {
        done(err, user);
      });
    });
    

    在上面的示例中,passport.serializeUser() 提供了一个函数,该函数接受两个参数,用户配置文件 (user) 和回调函数 (done)。回调函数的第二个参数是从数据库中恢复帐户所需的识别信息(user.id,但如果您使用的是 mongoDB,则可能是user._id)。这将在每个经过身份验证的请求上调用,并将识别信息存储在会话数据中(无论是在 cookie 中还是在您的 Redis 存储中)。

    passport.deserializeUser() 提供了一个函数,该函数也接受两个参数,标识信息 (id) 和回调函数 (done)。标识信息是在上一个请求中序列化为会话数据的内容 (user.id)。这里的回调函数需要用户配置文件作为它的第二个参数,或者在检索配置文件作为它的第一个参数时引发的任何错误。 User.findById() 函数是数据库中用户配置文件的查找函数。在此示例中,User 对象是具有 findById() 函数的猫鼬模型的实例。

    提供给passport.deserializeUser() 的函数在路由处理之前由护照中间件passport.session() 调用,以将用户配置文件(user)存储到req.user

    将 Redis 实现为会话存储

    使用 Redis 的目的是在服务器端存储会话数据,因此客户端存储的唯一数据是会话 ID。同样,这与您实现护照的方式无关,只要您为应用程序添加了会话支持,护照就不会关心会话数据的存储位置。 This previos question on stackoverflow 讲述如何实现 Redis

    【讨论】:

    • 我认为我的问题是我不知道 RedisStore 的等效 deserializeUser 是什么。会话存储已从我身上抽象出来,但现在我需要知道编写此函数的工作原理。您链接中的示例也没有显示反序列化函数的示例。
    • 如果您将用户配置文件存储在 Redis 中(除了单独存储的会话数据),您不会从 RedisStore 反序列化,您只是通过密钥检索用户配置文件/数据. Redis + Node.js - how do I retrieve the values
    • 对不起,我只是不明白。我没有在 Redis 中明确存储任何内容。我只是将中间件设置为使用 RedisStore。我在 Redis 中看到看起来像 sess:3343434 的键,我认为它们是 Express 创建的 id 的序列化值?至少在我编辑反序列化函数之前我是这样做的。登录现在可以工作,但会话没有被持久化。
    • 你最后的评论让我再次想到,如果没有用户配置文件或者我单独处理,我不需要覆盖反序列化。
    • 嗨@Weston,如果我有一个用户作为ServerModel 的子文档,如果我需要serverId 和userId 来查找用户,我该如何实现deserializeUser 函数?如何将多个参数传递给 deserializeUser 函数?谢谢!
    【解决方案2】:

    鉴于express-session 的以下配置,connect-redis 作为会话存储(使用 Express 4):

    redis = require('redis').createClient(6379, '127.0.0.1');
    session = require('express-session');
    RedisStore = require('connect-redis')(session);
    
    app.use(session({
      store: new RedisStore({
        client: redis
      }),
      secret: 's3cret',
      resave: true,
      saveUninitialized: true
    }));
    

    您可以只告诉passport 序列化整个用户对象,而不仅仅是用户ID。

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

    整个用户对象将与会话一起保存在 Redis 中,并针对每个请求以 req.user 的形式放置在请求中。

    【讨论】:

    • 其他 cmets 对理解很有帮助,但最终这对我来说是最好的解决方案,因为我也在使用 Redis,很好很容易
    • 您能否解释一下如果用户数据发生变化,您将如何更新他们?
    【解决方案3】:

    有点晚了,但我已经让这个视觉上的东西理解了

    1. 何时以及如何调用策略/本地/Facebook/etc 以及它如何到达 req.login 或 passport.serializeUser() 以及 done() 是什么?

    passport.authenticate() 调用您作为参数提供的相应策略,在那里您将req.body.passwordreq.body.username 与存储的数据库或内存中存储的密码和用户名匹配。如果用户发现您将其传递给 done() 作为第二个参数,否则您将 return false

    完成回调返回passport.authenticate()。如果 done 之前由用户调用(即 done(null,user); )而不是 req,logIn() 自动调用或由用户在幕后调用

    req.logIn() 呼叫passport.serializeUser()

    1. 什么是 passport.serializeUser 以及调用此函数后 user.some_key 会去哪里?

    您在序列化函数中完成的第二个参数中提供的用户对象的键保存在会话中,并用于通过反序列化函数检索整个对象。

    Serialize 函数确定来自用户对象的哪些数据应存储在会话中。 serializeUser 方法的结果以req.session.passport.user = {} 的形式附加到会话中,例如(因为我们提供 id 作为键)req.session.passport.user = {id:'xyz'}

    1. 什么是 passport.deserializeUser 以及它在工作流程中的什么位置?

    在反序列化函数中,您在反序列化函数的第一个参数中提供与序列化调用中的完成函数相同的用户对象键。所以你的整个对象都是在那个键的帮助下检索的。这里的键是 id(键可以是用户对象的任何键,即姓名、电子邮件等) 在 deSerialize 函数中,该键与内存数组/数据库或任何数据资源中的键匹配

    获取的对象作为req.user附加到请求对象

    id 键可以是用户对象的任意键,例如name,email

    视觉流

    passport.authenticate()-----------
                                     |  
                                     |  invokes 
                                    \./
           passport.use(new LocalStrategy(
                function(username, password, done) {
    
               // match req.body.username and req.body.password from any 
                  //data base or in memory array
                   if(user_is_found_and_pass_match)
                      done(null,user);--
                   else                   | *1-user passed
                                          |
                      done(null,false);---| *2-user not passed
           });                            | 
                                          |return back to
    passport.authenticate() <------------ |
                          |
                          |----- if user is passed in done() (*1) ,   
                                |
        req.login()   <--------- 
                  |
     //authenticate() middleware  may  invoke req.login() automatically.
                  |
                  | calls
                 \./  
     passport.serializeUser(function(user, done) {
            done(null, user.id); 
                         |
    //use 'id'to serialize, you can use other or user object itself
        });              |-->saved to session req.session.passport.user = {id:'..'}
                         |
                         |__________________
                                           |          
        passport.deserializeUser(function(id, done) {
                          ________________|
                          | 
            User.findById(id, function(err, user) {
                done(err, user);
                           |______________>user object ataches to the request as req.user
    
         });
          });
    

    这里的id键可以是用户对象的任意键,例如name,email

    【讨论】:

    • 我没有得到一件事,序列化正在存储 req.session.passport.user = {id:'xyz'} 这个。那么谁将用户存储在 mongo DB 中。如果我只想将会话存储在内存中怎么办?
    • @Mannu 看看现在是否更有意义
    • 对不起这个问题,使用“id”或“object”本身作为键有什么不同?
    • @kikko088 没有区别,如果您在序列化完成()函数中使用了完整对象,则可以在 mongodb 或任何源中找到完整用户。功能(错误,用户){完成(错误,用户);});})
    猜你喜欢
    • 2019-08-14
    • 2011-04-06
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    • 1970-01-01
    • 2017-04-03
    • 2023-04-06
    相关资源
    最近更新 更多