如何在 express 和 socket.io 中使用护照?
Posted
技术标签:
【中文标题】如何在 express 和 socket.io 中使用护照?【英文标题】:How to use passport with express and socket.io? 【发布时间】:2012-10-17 05:17:20 【问题描述】:我目前正在尝试为我的 node.js 应用程序设置一个基本的身份验证系统。现在我使用 express (3.0.0rc5)、passport (0.1.12) 和 socket.io (0.9.10) 以及 Mongoose 作为会话数据的存储。我也一直在玩 everyauth
,但我不喜欢使用 Promise。
目前情况:
通过护照(facebook 策略)的身份验证成功,重定向后在客户端上设置了一个session.sid
cookie,我可以在我的数据库中看到一个会话文档。我可以通过socket.handshake.headers.cookie
访问socket.io
中的会话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
来访问相应的电子邮件地址。
我该怎么做?任何指针都非常感谢。
【问题讨论】:
【参考方案1】:这是一个使用 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 管道的函数。它只需要三个参数:请求对象、响应对象和回调。
Socket.IO 的管道希望其中间件只接受两个参数,但是:套接字对象(在 socket.request 处包含请求对象)和回调。幸运的是 sessionMiddleware 不需要响应对象从存储中读取会话,因此我们只需将一个空对象作为第二个参数传递给它。
请注意,下面的一些 cmets 观察到此代码将会话呈现为只读。这是我们因 Socket.IO 没有正确的 response 对象而失去的功能。
在上面的示例中,我使用了 MongoDB 会话存储 (connect-mongo)。您可以使用任何适合您的会话存储。有关会话存储列表,请参阅 the Connect wiki。
【讨论】:
在 Express 部分中,当您将sessionMiddleware
传递给 app.use(...)
时,Express 将在收到请求时调用该函数。按照惯例,Express 向函数传递三个参数:请求对象、响应对象,然后在完成时调用next
函数。 Socket.IO v1.0 具有类似的范例,但它传递两个参数而不是三个:套接字对象和next
函数。您引用的行在两个 API 之间进行转换。
我收到一个 'new TypeError("Parameter 'url' must be a string, not " + typeof url)' 错误,因为与 socket.io 1.1.0 的路径名不匹配,源自该行'sessionMiddleware(socket.request, , next);' 并在 'session (../server/node_modules/express-session/index.js:98:24)' 中提出。任何建议表示赞赏!
@AlexMills 会话信息由 sessionMiddleware 在内部从您的会话存储中检索。事实上,这正是 sessionMiddleware 对 Express 的工作;我们只是操纵它为 Socket.IO 工作。
如果你把护照中间件也放入socket.io,那么你可以找到client.request.user
少一点挖掘,不确定这是否有任何优势......
socket.request.session.passport
在认证后未定义除非重新连接socket,问题是我在认证前连接了socket,我该如何解决?【参考方案2】:
我让它工作了。我必须做的是访问 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
【讨论】:
【参考方案3】:您可以使用express-socket.io-session。您可以使用 socket.handshake.session.passport.user
获取护照会话用户。它支持读取和写入会话存储。
注意:适用于 express > 4.0.0 和 socket.io > 1.0.0,并且不会向后兼容
【讨论】:
您好,您能提供更多步骤吗? 我明白了,但是您如何获取用户对象而不是 id。我想您可以进行数据库查找,但使用护照它会自动进行会话和除颤。使用该方法,它只显示 ID以上是关于如何在 express 和 socket.io 中使用护照?的主要内容,如果未能解决你的问题,请参考以下文章
如何在同一个端口上运行 express 和 socket.io
在 node.js 中,如何使用 socket.io 和 express 设置 redis?具体使用 RedisStore()
如何使用 Express、AngularJS、Socket.io 广播和获取通知?