2015-01-15 5 views
7

Desidero condividere la sessione del server tra un'app NodeJs e un'app PHP utilizzando Redis. Ho preso la maggior parte del codice da questo gist. CodiceCome condividere la sessione tra NodeJs e PHP usando Redis?

NodeJs:

app.use(session({ 
    store: new RedisStore({prefix: 'session:php:'}), 
    name: 'PHPSESSID', 
    secret: 'node.js' 
})); 

app.use(function(req, res, next) { 
    req.session.nodejs = 'node.js!'; 
    res.send(JSON.stringify(req.session, null, ' ')); 

}); 

Ed Uscite:

{ 
    "cookie": { 
     "originalMaxAge": null, 
     "expires": null, 
     "httpOnly": true, 
     "path": "/" 
    }, 
    "passport": {}, 
    "nodejs": "node.js!" 
} 

codice PHP (io uso redis-session-php e Predis):

require('redis-session-php/redis-session.php'); 
RedisSession::start(); 

$_SESSION['php'] = 'php'; 

if (!isset($_SESSION["cookie"])) { 
    $_SESSION["cookie"] = array(); 
} 

var_dump($_SESSION); 

Ed Uscite:

array(2) { 
    ["php"] => string(3) "php" 
    ["cookie"] => array(0) { } 
} 

Il problema: mi si aspetterebbe entrambe le sessioni per lo stesso aspetto, ma non lo fanno (l'applicazione viene eseguita sullo stesso dominio). L'impostazione dei valori con set() da Predis\Client funziona (ma i valori non saranno sulla variabile di sessione). Ho trovato this code che penso avrebbe funzionato, usando set() e get(), ma ritengo che potrebbe complicare eccessivamente il codice.

Sai cosa sto facendo male?

+0

qualcosa sembra pesce con il modulo i Redis php. Mi aspetto di vedere altre opzioni di configurazione per la connessione ai redis. puoi mostrarci la configurazione per PHP Redis (principalmente quello che stai passando nel costruttore – geggleto

+0

Non sto passando nulla, per impostazione predefinita se non vengono passati argomenti si assume che sia '127.0.0.1' e '6379' come host predefinito e porta con un timeout di connessione di 5 secondi –

+0

Wll nel tuo nodo.js stai passando una chiave segreta ... Non vedo l'opzione nel tuo codice per php. potrebbe essere che non si connette allo stesso negozio redis? – geggleto

risposta

12

Sono l'autore della sostanza. Il codice ha funzionato fino a quando express-session ha iniziato a forzare i cookie firmati e ha iniziato a implementarli in un modo diverso.

Ho aggiornato la sostanza per funzionare con l'ultima versione di express-session. Una copia della Gist segue per comodità:

app.js:

var express = require('express'), 
    app = express(), 
    cookieParser = require('cookie-parser'), 
    session = require('express-session'), 
    RedisStore = require('connect-redis')(session); 

app.use(express.static(__dirname + '/public')); 
app.use(function(req, res, next) { 
    if (~req.url.indexOf('favicon')) 
    return res.send(404); 
    next(); 
}); 
app.use(cookieParser()); 
app.use(session({ 
    store: new RedisStore({ 
    // this is the default prefix used by redis-session-php 
    prefix: 'session:php:' 
    }), 
    // use the default PHP session cookie name 
    name: 'PHPSESSID', 
    secret: 'node.js rules', 
    resave: false, 
    saveUninitialized: false 
})); 
app.use(function(req, res, next) { 
    req.session.nodejs = 'Hello from node.js!'; 
    res.send('<pre>' + JSON.stringify(req.session, null, ' ') + '</pre>'); 
}); 

app.listen(8080); 

app.php:

<?php 
// this must match the express-session `secret` in your Express app 
define('EXPRESS_SECRET', 'node.js rules'); 

// ==== BEGIN express-session COMPATIBILITY ==== 
// this id mutator function helps ensure we look up 
// the session using the right id 
define('REDIS_SESSION_ID_MUTATOR', 'express_mutator'); 
function express_mutator($id) { 
    if (substr($id, 0, 2) === "s:") 
    $id = substr($id, 2); 
    $dot_pos = strpos($id, "."); 
    if ($dot_pos !== false) { 
    $hmac_in = substr($id, $dot_pos + 1); 
    $id = substr($id, 0, $dot_pos); 
    } 
    return $id; 
} 
// check for existing express-session cookie ... 
$sess_name = session_name(); 
if (isset($_COOKIE[$sess_name])) { 
    // here we have to manipulate the cookie data in order for 
    // the lookup in redis to work correctly 

    // since express-session forces signed cookies now, we have 
    // to deal with that here ... 
    if (substr($_COOKIE[$sess_name], 0, 2) === "s:") 
    $_COOKIE[$sess_name] = substr($_COOKIE[$sess_name], 2); 
    $dot_pos = strpos($_COOKIE[$sess_name], "."); 
    if ($dot_pos !== false) { 
    $hmac_in = substr($_COOKIE[$sess_name], $dot_pos + 1); 
    $_COOKIE[$sess_name] = substr($_COOKIE[$sess_name], 0, $dot_pos); 

    // https://github.com/tj/node-cookie-signature/blob/0aa4ec2fffa29753efe7661ef9fe7f8e5f0f4843/index.js#L20-L23 
    $hmac_calc = str_replace("=", "", base64_encode(hash_hmac('sha256', $_COOKIE[$sess_name], EXPRESS_SECRET, true))); 
    if ($hmac_calc !== $hmac_in) { 
     // the cookie data has been tampered with, you can decide 
     // how you want to handle this. for this example we will 
     // just ignore the cookie and generate a new session ... 
     unset($_COOKIE[$sess_name]); 
    } 
    } 
} else { 
    // let PHP generate us a new id 
    session_regenerate_id(); 
    $sess_id = session_id(); 
    $hmac = str_replace("=", "", base64_encode(hash_hmac('sha256', $sess_id, EXPRESS_SECRET, true))); 
    // format it according to the express-session signed cookie format 
    session_id("s:$sess_id.$hmac"); 
} 
// ==== END express-session COMPATIBILITY ==== 



require('redis-session-php/redis-session.php'); 
RedisSession::start(); 

$_SESSION["php"] = "Hello from PHP"; 
if (!isset($_SESSION["cookie"])) 
    $_SESSION["cookie"] = array(); 

echo "<pre>"; 
echo json_encode($_COOKIE, JSON_PRETTY_PRINT); 
echo json_encode($_SESSION, JSON_PRETTY_PRINT); 
echo "</pre>"; 

?> 
+0

Questa riga '$ _COOKIE [$ sess_name] = substr ($ _ COOKIE [$ sess_name], 0, $ dot_pos)' non renderà il cookie di sessione uguale a '" "'? –

+0

No. 'express-session' il formato dei cookie è 's: <32-byte id>. <32-byte id hmac>'. Quindi tutta la linea sta facendo è impostare il valore del cookie di sessione solo sulla parte id a 32 byte. Il codice potrebbe probabilmente essere semplificato un po 'da quando ho incluso la funzione 'express_mutator', ma il valore hmac dovrebbe ancora essere verificato. – mscdex

+0

Ma se cambio tra PHP e NodeJs, ogni volta verrà generato un nuovo ID di sessione, perché uno di questi sta impostando il cookie come 's: <32-byte id>. <32-byte id hmac>' e l'altro come '<32-byte id>', a destra ? Come posso risolvere questo problema, utilizzando 2 cookie separati? –