你如何向外行解释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); }); });

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

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

任何帮助都是感激的。


当前回答

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);
          });
        });

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。

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

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', {

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

要知道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

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

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

req.session.passport.user

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

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

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

希望这能简化。