【问题标题】:How to share session between NodeJs and PHP using Redis?如何使用 Redis 在 NodeJs 和 PHP 之间共享会话?
【发布时间】:2015-03-14 01:00:53
【问题描述】:

我想使用RedisNodeJs 应用和PHP 应用之间共享服务器会话。我从这个gist 中获取了大部分代码。

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, '  ') );

});

它输出:

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

PHP 代码(我使用redis-session-phpPredis):

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

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

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

var_dump($_SESSION);

它输出:

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

问题: 我希望这两个会话看起来相同,但它们不是(应用程序在同一个域上运行)。使用来自Predis\Clientset() 设置值是可行的(但这些值不会在会话变量上)。我发现this code 我认为可以使用set()get(),但我觉得这会使代码过于复杂。

你知道我做错了什么吗?

【问题讨论】:

  • php redis 模块似乎有些可疑。我希望看到更多用于连接到 redis 的配置选项。你能告诉我们 PHP Redis 的配置吗(主要是你传递给构造函数的内容
  • 我没有传递任何东西,默认情况下,如果没有传递任何参数,它假定127.0.0.16379 作为默认主机和端口,连接超时为 5 秒。
  • Wll 在您的 node.js 中您正在传递一个密钥...我在您的 php 代码中没有看到该选项。可能是它没有连接到同一个redis存储?

标签: php node.js session redis


【解决方案1】:

我是要点的作者。代码一直有效,直到 express-session 开始强制签名 cookie 并开始以不同的方式实现它们。

我已更新要点以使用最新版本的express-session。为方便起见,附上一份要点:

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>";

?>

【讨论】:

  • 这一行 $_COOKIE[$sess_name] = substr($_COOKIE[$sess_name], 0, $dot_pos); 不会使会话 cookie 等于 "" ?
  • 没有。 express-session 的 cookie 格式为 s:&lt;32-byte id&gt;.&lt;32-byte id hmac&gt;。因此,该行所做的就是将会话 cookie 值设置为 32 字节的 id 部分。由于我包含了express_mutator 函数,因此代码可能会简化一点,但仍应验证hmac 值。
  • 但是如果我在 PHP 和 NodeJs 之间切换,每次都会生成一个新的会话 id,因为其中一个将 cookie 设置为s:&lt;32-byte id&gt;.&lt;32-byte id hmac&gt;,另一个设置为&lt;32-byte id&gt;,对吗?我该如何解决这个问题,使用 2 个单独的 cookie?
  • 不,应该只有一个会话 cookie。我亲自测试了代码,让每一方创建会话,另一方读取会话。这两种情况都可以正常工作。 s:&lt;32-byte id&gt;.&lt;32-byte id hmac&gt; 在 PHP 和节点中始终设置为会话 cookie 值。 &lt;32-byte id&gt; 是存储在 redis 中的 key。
  • 感谢您分享您的代码。我似乎无法让这个工作。我可以从每个单独的应用程序中看到会话数据,但它们不会交叉。我不应该能够在节点中看到 PHP 会话数据吗?反之亦然?我错过了什么?
猜你喜欢
  • 2016-01-12
  • 2014-07-10
  • 1970-01-01
  • 2017-04-08
  • 2011-12-20
  • 2014-07-29
  • 2011-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多