你如何向外行解释Passport的序列化和反序列化方法的工作流程?

Where does user.id go after passport.serializeUser has been called? We are calling passport.deserializeUser right after it where does it fit in the workflow? // used to serialize the user for the session passport.serializeUser(function(user, done) { done(null, user.id); // where is this user.id going? Are we supposed to access this anywhere? }); // used to deserialize the user passport.deserializeUser(function(id, done) { User.findById(id, function(err, user) { done(err, user); }); });

我还没想明白呢。我有一个完整的工作应用程序,没有遇到任何类型的错误。

我只是想知道这里到底发生了什么?

任何帮助都是感激的。


用户在哪里。我去拿护照。serializeUser是否被调用?

用户id(作为done函数的第二个参数提供)保存在会话中,稍后用于通过deserializeUser函数检索整个对象。

serializeUser决定会话中应该存储用户对象的哪些数据。serializeUser方法的结果作为req.session.passport.user ={}附加到会话。例如,在这里,它将是(因为我们提供用户id作为键)req.session.passport.user = {id: 'xyz'}

我们在它后面调用passport。deserializeuser它在工作流中的位置是什么?

deserializeUser的第一个参数对应给done函数的user对象的键(见1.)。你的整个对象都是在这个键的帮助下被检索的。这里的键是用户id(键可以是用户对象的任何键,例如名称,电子邮件等)。 在deserializeUser中,该键与内存中的数组/数据库或任何数据资源匹配。

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

视觉流

passport.serializeUser(function(user, done) {
    done(null, user.id);
});              │
                 │ 
                 │
                 └─────────────────┬──→ saved to session
                                   │    req.session.passport.user = {id: '..'}
                                   │
                                   ↓           
passport.deserializeUser(function(id, done) {
                   ┌───────────────┘
                   │
                   ↓ 
    User.findById(id, function(err, user) {
        done(err, user);
    });            └──────────────→ user object attaches to the request as req.user   
});

对于任何使用可可豆和可可豆护照的人:

要知道serializeUser方法中设置的用户的键(通常是该用户的唯一id)将存储在:

this.session.passport.user

当你在deserializeUser中设置done(null, user)时,其中“user”是数据库中的某个用户对象:

this.req.user 或 this.passport.user

出于某种原因。在您的deserializeUser方法中调用done(null, user)时,用户Koa上下文永远不会被设置。

所以你可以在调用app.use(passport.session())之后编写自己的中间件,把它放在这里。用户如下:

app.use(function * setUserInContext (next) {
  this.user = this.req.user
  yield next
})

如果您不清楚serializeUser和deserializeUser是如何工作的,请在twitter上联系我。@yvanscher


Passport使用serializeUser函数将用户数据(身份验证成功后)持久化到会话中。函数deserializeUser用于从会话中检索用户数据。

serializeUser和deserializeUser函数都检查传递给它们的第一个参数,如果它是函数类型,serializeUser和deserializeUser函数什么都不做,而是将这些函数放在一个函数堆栈中,之后将被调用(当传递的第一个参数不是函数类型时)。 在会话中进行身份验证后,Passport需要以下设置来保存用户数据:

app.use(session({ secret: "cats" }));
app.use(passport.initialize());
app.use(passport.session());

使用中间件的顺序很重要。重要的是,当一个新的授权请求开始时,会发生什么:

session middleware creates session (using data from the sessionStore). passport.initialize assigns _passport object to request object, checks if there's a session object, and if it exists, and field passport exists in it (if not - creates one), assigns that object to session field in _passport. At the end, it looks, like this: req._passport.session = req.session['passport'] So, session field references object, that assigned to req.session.passport. passport.session looks for user field in req._passport.session, and if finds one, passes it to deserializeUser function and calls it. deserializeUser function assigns req._passport.session.user to user field of request object (if find one in req._passport.session.user). This is why, if we set user object in serializeUser function like so: passport.serializeUser(function(user, done) { done(null, JSON.strignify(user)); }); We then need to parse it, because it was saved as JSON in user field: passport.deserializeUser(function(id, done) { // parsed user object will be set to request object field `user` done(err, JSON.parse(user)); });

所以,当你设置Passport时,首先调用deserializeUser函数,把你的回调放到_deserializers函数堆栈中。第二次,它会在passport中被调用。会话中间件将用户字段分配给请求对象。这也会在分配user字段之前触发回调(我们放在passport.deserializeUser()中)。

serializeUser函数在设置Passport时首先调用(类似于deserializeUser函数),但它将用于序列化用户对象以保存在会话中。第二次,它将在login/ login(别名)方法中被调用,由Passport附加,并用于在会话中保存用户对象。serializeUser函数还检查_serializers堆栈中已经推入它的函数(其中一个是在我们设置Passport时添加的):

passport.serializeUser(function(user, done) ...

并调用它们,然后将用户对象(经过标记)或用户id分配给req._passport.session.user。重要的是要记住session字段在req中直接引用passport字段。会话对象。这样用户就保存在会话中了(因为req._passport. conf。会话引用对象req.session。Passport和req._passport。会话在每个传入请求中通过护照进行修改。初始化中间件)。 当请求结束时,请求。会话数据将存储在sessionStore中。

成功授权后,当第二个请求开始时,会发生什么:

会话中间件从sessionStore获取会话,我们的用户数据已经保存在sessionStore中 护照。初始化检查是否有会话,并将req.session.passport分配给req._passport.session 护照。会话检查req._passport.session。用户并反序列化它。在此阶段(如果req._passport.session. conf。用户是真实的),我们将有需求。req.isAuthenticated()返回true。


你可以用这个代码升级旧的序列化和反序列化,请在这个帖子上找到新的解决方案。

        passport.serializeUser(function(user, cb) {
          process.nextTick(function() {
            cb(null, { id: user.id, username: user.username });
          });
        });
        
        passport.deserializeUser(function(user, cb) {
          process.nextTick(function() {
            return cb(null, user);
          });
        });

基本上,我们只是使用序列化器将user-id存储在会话中,当我们需要用户模型实例时,我们使用user-id在数据库中搜索,这是使用反序列化器完成的。

只要会话是活动的,并且用户通过了身份验证,

req.session.passport.user

将始终对应于用户模型实例。

如果我们不将user-id保存到会话中,如果有重定向,我们将无法知道用户是否已经过身份验证。

一旦用户通过身份验证,就会设置req.session.passport.user。 因此,所有未来的请求都将知道用户已经过身份验证。

希望这能简化。


所以我在这个问题上卡住了很长一段时间,所以我做了一个快速的设置文件,这样就不会有人再被这个垃圾卡住了

passportmgmt.js

const localStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const User = require('./db-schemas/models').UserModel;
/*^^^^^^^^^^^ delete above import and modify below definition as needed 
const mongoose = require('mongoose')
mongoose.connect("mongodb://localhost:27017/database_name");

const UserSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    }
});
const UserModel = mongoose.model('collection_name', UserSchema);
^^^^^^^^^^^^^^^ Change UserModel to User */
const passport = require('passport');

function passportSetupUwU(app) {
    const cookieParser = require('cookie-parser');
    const session = require('express-session');
    const MongoDBStore = require('connect-mongodb-session')(session);

    app.use(cookieParser());
    app.set('trust proxy', 1);
    app.use(session({
    secret: "secret",
    resave: false,
    saveUninitialized: false,
    store: new MongoDBStore({
        uri: "mongodb://localhost:27017/database_name",
        collection: "collection_name"
    })
    }));

    // Passport.js
    app.use(passport.initialize());
    app.use(passport.session());
}

passport.serializeUser(function(user, cb) {
    console.log("serializing user uwu:" + JSON.stringify(user))
    process.nextTick(function() {
        return cb(null, user.id)
    })
})

passport.deserializeUser(function (id, cb) {
    console.log("deserializing user owo:" + JSON.stringify(id))
    User.findOne({id:id}, function (err, user) {
        if (err) { return cb(err)}
        return cb(null, user);
    })
});

passport.use(new localStrategy(function (username, password, done) {
    console.log("attempted login with %s:%s", username, password);
    User.findOne({ username: username }, function (err, user) {
        if (err) return done(err);
        if (!user) return done(null, false, { message: 'No such user exists.' });

        bcrypt.compare(password, user.password, function (err, res) {
            if (err) return done(err);
            if (res === false) return done(null, false, { message: 'Incorrect password.' });
            
            return done(null, user);
        });
    });
}));

module.exports = {setup: passportSetupUwU}

现在只需导入并在app.js中运行passportSetupUwU函数,你就可以开始了:

app.js exerpt

// some example middleware and stuff i happened to have
const PPSetup = require('./passportmgmt').setup
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));

//
// <- PUT IT LIKE HERE OR SOMETHING
//
PPSetup(app);

// some example routes and api endpoints i happened to have
app.use('/api/', apiRouter);
apiRouter.use('/', indexRouter);
apiRouter.use('/users', usersRouter);

apiRouter.post('/login', passport.authenticate('local', {

Passport的序列化和反序列化方法的可视化工作流:

User logs in to your application and provides their credentials. Passport uses the serializeUser function to take the user object and convert it into a session object. Passport stores the session object on the server and sends a session cookie to the user's browser. 4.User makes a request to your application. 5.Passport uses the deserializeUser function to take the session object and convert it back into a user object. Passport passes the user object to your application, which can then use it to determine if the user is authenticated or not. If the user is authenticated, your application will allow them to access the requested resource. If the user is not authenticated, your application will deny access and redirect them to the login page.

下面是工作流的可视化表示:

    +------------+             +---------------+
|            |             |               |
| User Login |             | Session Object |
|            |    serializeUser    |               |
+-----+------+   ------------------>   +---------+
      |                                 |           |
      |                                 | Store on  |
      |                                 | Server    |
      |                                 |           |
+-----+------+   <------------------   +---------+
|            |             |               |
| User Request |    deserializeUser    |               |
|            |   ------------------>   |             |
+------------+             |               +-----------+
      |                   |
      |                   |
      |                   |
+-----+------+   <------------------   +---------+
|            |             |               |
| Authenticated |             |     User     |
|  or not       |             |             |
|            |             |               |
+------------+             +---------------+

我希望这有助于阐明Passport的序列化和反序列化方法的工作流程!