Created
August 2, 2012 07:35
-
-
Save MiLk/3234912 to your computer and use it in GitHub Desktop.
Authentification par cookie avec Express.js 3 & socket.io
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var express = require('express') | |
| , app = express.createServer() | |
| , cookie = require('cookie') | |
| , io = require('socket.io') | |
| , _ = require('underscore')._ | |
| , redis = require('redis') | |
| , dbmysql = require('db-mysql') | |
| , mysql = new dbmysql.Database({ | |
| hostname: '', | |
| port: 3306, | |
| user: '', | |
| password: '', | |
| database: '', | |
| initCommand: 'SET NAMES utf8' | |
| }).on('error', function(error) { | |
| console.log('ERROR: ' + error); | |
| }).on('ready', function(server) { | |
| console.log('Connected to ' + server.hostname + ' (' + server.version + ')'); | |
| }); | |
| var sessionStore = new express.session.MemoryStore({ reapInterval: 60000 * 10 }); | |
| app.configure(function(){ | |
| app.set('views', __dirname + '/views'); | |
| app.set('view engine', 'ejs'); | |
| app.use(express.cookieParser()); | |
| app.use(express.session({ | |
| "secret": "secret", | |
| "store": sessionStore, | |
| "key": 'express.sid', | |
| })); | |
| app.use(express.bodyParser()); | |
| app.use(express.methodOverride()); | |
| app.use(app.router); | |
| app.use(express.static(__dirname + '/public')); | |
| }); | |
| app.configure('development', function(){ | |
| app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); | |
| }); | |
| app.configure('production', function(){ | |
| app.use(express.errorHandler()); | |
| }); | |
| function requireLogin (req, res, next) { | |
| if (req.session.username) { | |
| // User is authenticated, let him in | |
| next(); | |
| } else { | |
| // Otherwise, we redirect him to login form | |
| res.redirect("/login"); | |
| } | |
| } | |
| function validateLogin(username,password,callback) { | |
| mysql.connect(function(err){ | |
| if(err) { console.log(err); callback(false,null); } | |
| var res = mysql.query('SELECT * FROM sf_guard_user WHERE username LIKE "'+mysql.escape(username)+'" AND password LIKE SHA1(CONCAT(salt,"'+mysql.escape(password)+'"))') | |
| .execute(function(error, rows, cols) { | |
| if (error) { | |
| console.log('ERROR: ' + error); | |
| callback(false,null); | |
| } | |
| callback(_.size(rows) == 1, rows[0]); | |
| }); | |
| }); | |
| } | |
| app.get("/login", function (req, res) { | |
| res.render("login", { "username": req.session.username, "error": null }); | |
| }); | |
| app.post("/login", function (req, res) { | |
| var options = { "username": req.body.username, "error": null }; | |
| if (!req.body.username) { | |
| options.error = "User name is required"; | |
| res.render("login", options); | |
| } else if (!req.body.password) { | |
| options.error = "Password is required"; | |
| res.render("login",options); | |
| } else if (req.body.username == req.session.username) { | |
| // User has not changed username, accept it as-is | |
| res.redirect("/"); | |
| } else if (!req.body.username.match(/^[a-zA-Z0-9\-_]{3,}$/)) { | |
| options.error = "User name must have at least 3 alphanumeric characters"; | |
| res.render("login", options); | |
| } else { | |
| validateLogin(req.body.username,req.body.password, function(auth,account){ | |
| if(!auth) | |
| { | |
| options.error = "Username or password is invalid."; | |
| res.render("login", options); | |
| } else { | |
| req.session.username = req.body.username; | |
| req.session.account = account.id; | |
| res.redirect("/"); | |
| } | |
| }); | |
| } | |
| }); | |
| app.get('/', [requireLogin], function(req, res){ | |
| res.render('index'); | |
| }); | |
| io = io.listen(app.listen(process.env.npm_package_config_port || 3000, function(){ | |
| console.log("Express server listening on port %d in %s mode", process.env.npm_package_config_port || 3000, app.settings.env); | |
| })); | |
| var RedisStore = require('socket.io/lib/stores/redis') | |
| , pub = redis.createClient() | |
| , sub = redis.createClient() | |
| , client = redis.createClient(); | |
| io.configure(function(){ | |
| io.enable('browser client minification'); // send minified client | |
| io.enable('browser client etag'); // apply etag caching logic based on version number | |
| io.enable('browser client gzip'); // gzip the file | |
| io.set('log level', 1); // reduce logging | |
| io.set('transports', [ // enable all transports (optional if you want flashsocket) | |
| 'websocket' | |
| // , 'flashsocket' | |
| , 'htmlfile' | |
| , 'xhr-polling' | |
| , 'jsonp-polling' | |
| ]); | |
| io.set('store', new RedisStore({ | |
| redisPub : pub | |
| , redisSub : sub | |
| , redisClient : client | |
| })); | |
| }); | |
| io.sockets.authorization(function (handshakeData, callback) { | |
| // Read cookies from handshake headers | |
| var cookies = cookie.parse(handshakeData.headers.cookie); | |
| // We're now able to retrieve session ID | |
| var sessionID = cookies['express.sid']; | |
| // No session? Refuse connection | |
| if (!sessionID) { | |
| callback('No session', false); | |
| } else { | |
| // Store session ID in handshake data, we'll use it later to associate | |
| // session with open sockets | |
| handshakeData.sessionID = sessionID; | |
| // On récupère la session utilisateur, et on en extrait son username | |
| // Hack pour avoir le bon format de sessionID | |
| var mySessionID = handshakeData.mySessionID = sessionID.substr(2,sessionID.indexOf('.')-2); | |
| sessionStore.get(mySessionID, function (err, session) { | |
| if (!err && session && session.username) { | |
| // On stocke ce username dans les données de l'authentification, pour réutilisation directe plus tard | |
| handshakeData.username = session.username; | |
| // OK, on accepte la connexion | |
| callback(null, true); | |
| } else { | |
| // Session incomplète, ou non trouvée | |
| callback(err || 'User not authenticated', false); | |
| } | |
| }); | |
| } | |
| }); | |
| var connections = {}; | |
| io.sockets.on('connection', function (socket) { | |
| var sessionID = socket.handshake.sessionID; // Store session ID from handshake | |
| console.log(socket.handshake); | |
| // this is required if we want to access this data when user leaves, as handshake is | |
| // not available in "disconnect" event. | |
| socket.set('username', socket.handshake.username, function(){ | |
| console.log(socket.handshake.username + ' connected !'); | |
| }); // Same here, to allow event "bye" with username | |
| if ('undefined' == typeof connections[sessionID]) { | |
| connections[sessionID] = { "length": 0 }; | |
| // First connection | |
| } | |
| // Add connection to pool | |
| connections[sessionID][socket.id] = socket; | |
| connections[sessionID].length ++; | |
| // do sth | |
| socket.on('disconnect', function () { | |
| // do sth | |
| var userConnections = connections[sessionID]; | |
| if (userConnections.length && userConnections[socket.id]) { | |
| // Forget this socket | |
| userConnections.length --; | |
| delete userConnections[socket.id]; | |
| } | |
| if (userConnections.length == 0) { | |
| // No more active sockets for this user: say bye | |
| console.log(socket.get('username') + ' disconnected !'); | |
| } | |
| }); | |
| socket.on('error', function(err) { | |
| console.log(err); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment