55

現在、node.js アプリの基本認証システムをセットアップしようとしています。今のところ、私は Express (3.0.0rc5)、passport (0.1.12)、socket.io (0.9.10) を Mongoose と共にセッション データのストアとして使用しています。私も遊んでいましeveryauthたが、約束を扱うのは好きではありませんでした。

現在の状況:

パスポート (Facebook 戦略) による認証は成功し、session.sidリダイレクト後にクライアントに Cookie が設定され、データベースでセッション ドキュメントを確認できます。からセッション Cookie にアクセスできsocket.ioますsocket.handshake.headers.cookie

パスポートの概念を正しく理解していれば、成功後にauthentication passport.serializeUserが呼び出され、セッションに情報を追加できるようになります。私の場合、最も重要な情報は電子メールであるため、シリアライザーを次のように設定します。

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

これで、socket.io イベントの Cookie 情報のみを使用して、セッションから電子メール アドレスを抽出する必要があるところまで来ました。

var connect = require('connect'),
    parseSignedCookie = connect.utils.parseSignedCookie,
    cookie            = require('express/node_modules/cookie');

io.on('connection', function(socket) {
    if(socket.handshake.headers.cookie) {
        var cookie = cookie.parse(socket.handshake.headers.cookie);
        var sessionID = parseSignedCookie(cookie['connect.sid'], 'secret');
    }
});

passport.deserializeUser(function(id, done) {
    // so, what is supposed to be done here?
});

したがって、私が間違っていなければdeserializeUser、対応するメールアドレスにアクセスするために使用するタスクになります。

どうすればいいですか?どんな指針も高く評価されます。

4

3 に答える 3

72

Socket.IO 1.0 と Express 4.0を使用したソリューションを次に示します。パトリックの答えと精神的に似ています。秘訣は、Socket.IO 1.0 には新しいミドルウェア API があるため、Express のミドルウェアをラップして、セッション ストアの低レベルの実装を掘り下げることなく Socket.IO パイプラインに入れることができるということです。

// Set up the Session middleware using a MongoDB session store
expressSession = require("express-session");
var sessionMiddleware = expressSession({
    name: "COOKIE_NAME_HERE",
    secret: "COOKIE_SECRET_HERE",
    store: new (require("connect-mongo")(expressSession))({
        url: "mongodb://localhost/DATABASE_NAME_HERE"
    })
});

// Set up the Express server
var app = require("express")()
    .use(sessionMiddleware)
    .use(passport.initialize())
    .use(passport.session())
    // ... more middleware ...
    .listen(8000);

// Set up the Socket.IO server
var io = require("socket.io")(app)
    .use(function(socket, next){
        // Wrap the express middleware
        sessionMiddleware(socket.request, {}, next);
    })
    .on("connection", function(socket){
        var userId = socket.request.session.passport.user;
        console.log("Your User ID is", userId);
    });

変数sessionMiddlewareは、Express パイプラインに直接適合するように設計された関数です。リクエスト オブジェクト、レスポンス オブジェクト、コールバックの 3 つの引数を取ります。

ただし、Socket.IO のパイプラインは、そのミドルウェアが 2 つの引数のみを受け取ることを想定しています。それは、ソケット オブジェクト ( socket.requestに要求オブジェクトを含む) とコールバックです。幸いなことに、 sessionMiddlewareは応答オブジェクトがストアからセッションを読み取る必要がないため、2 番目の引数として空のオブジェクトを渡すだけです。

以下のいくつかのコメントは、このコードがセッションを読み取り専用にすることを観察していることに注意してください。これは、 Socket.IOで適切な応答オブジェクトを持たないことによって失われる機能です。

上記の例では、MongoDB セッション ストア (connect-mongo) を使用しています。好みに合った任意のセッション ストアを使用できます。セッション ストアのリストについては、Connect wikiを参照してください。

于 2014-07-21T07:14:59.213 に答える
34

私はそれを働かせました。私がしなければならなかったのは、sessionStore にアクセスすることです。他の誰かがこの特定の問題に遭遇した場合のコードは次のとおりです。

// # app.js

var express     = require('express'),
    routes      = require('./routes'),
    http        = require('http'),
    path        = require('path'),
    app         = express(),
    passport    = require('passport'),
    SessionMongoose = require("session-mongoose"),
    mongooseSessionStore = new SessionMongoose({
        url: "mongodb://localhost/login",
        interval: 120000 
    });

var config       = require('game/config.js'), // holds my whole server configuration
    server       = require('game/lib/server.js');

// save sessionStore to config for later access
config.setSessionStore(mongooseSessionStore);

// configure express to use passport and mongooseSessionStore
app.configure(function(){
    app.set('port', config.port);
    app.set('env', config.environment);
    app.set('dbPrefix', config.dbPrefix);
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(express.favicon());
    app.use(express.bodyParser());
    app.use(express.cookieParser());
    app.use(express.session({secret : 'totallysecret', store : mongooseSessionStore })),
    app.use(express.methodOverride());
    app.use(passport.initialize());
    app.use(passport.session());    
    app.use(app.router);
    app.use(express['static'](path.join(__dirname, 'public')));
});

app.get('/', routes.index);

app.get('/auth/facebook', passport.authenticate('facebook', { scope: 'email' }));
app.get('/auth/facebook/callback', 
    passport.authenticate('facebook', { successRedirect: '/',
                                        failureRedirect: '/' })); 

// #### Init httpserver
var httpServer = http.createServer(app);
httpServer.listen(app.get('port'));

// #### Server startup
server.init(httpServer);

私のシリアライゼーション関数は次のように単純に見えます:

passport.serializeUser(function(user, done) {
    // saves user.email to session.passport.user
    done(null, user.email);
});

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

そして最後に socket.io の実装:

var util              = require('util'),
    connect           = require('connect'),
    parseSignedCookie = connect.utils.parseSignedCookie,
    cookie            = require('express/node_modules/cookie'),
    io                = require('socket.io').listen(httpServer);

var config = require('../config.js');  

io.configure(function () {
    io.set('authorization', function (data, callback) {
        if(data.headers.cookie) {
            // save parsedSessionId to handshakeData
            data.cookie = cookie.parse(data.headers.cookie);
            data.sessionId = parseSignedCookie(data.cookie['connect.sid'], 'totallysecret');
        }
        callback(null, true);
    });

    io.on('connection', function(socket) {
        // reference to my initialized sessionStore in app.js
        var sessionStore = config.sessionStore;
        var sessionId    = socket.handshake.sessionId;

        sessionStore.get(sessionId, function(err, session) {
            if( ! err) {
                if(session.passport.user) {
                    console.log('This is the users email address %s', session.passport.user);
            }
        });
    });
});

アクセスできる session-mongoose モジュールを使用します。

sessionStore.get(sessionId, callback)
sessionStore.set(sessionId, data, callback) 
sessionStore.destroy(sessionId, callback) 
sessionStore.all(callback)    // returns all available sessions
sessionStore.clear(callback)  // deletes all session data
sessionStore.length(callback) // returns number of sessions in the 
于 2012-10-27T09:10:06.880 に答える