简体   繁体   English

使用Redis与Node.js的CakePhp 3.x共享会话

[英]CakePhp 3.x Sharing session with nodejs using redis

Alright, I have been following this example of sharing a session between php and nodejs using redis : https://gist.github.com/mscdex/9507b0d8df42e0aec825 好吧,我一直在关注这个使用redis在php和nodejs之间共享会话的示例: https ://gist.github.com/mscdex/9507b0d8df42e0aec825

I got that working beautifully, but now I am trying to see how I would go about it with CakePhp. 我的工作很漂亮,但是现在我想看看如何使用CakePhp进行操作。 I have a few questions about how I would go about it: 我对如何处理有一些疑问:

  1. Should I just create a new session handler and inside the constructor run the session configurations that I need to do before the session start?? 我是否应该只创建一个新的会话处理程序,并在构造函数内部运行会话开始之前需要做的会话配置?

  2. Should I just create a new Session.php class extended from the one provided by CakePhp? 我应该只创建一个由CakePhp提供的类扩展的新Session.php类吗? If I do this, how would I get the application to use it? 如果这样做,我将如何使应用程序使用它?

Code linked above: 上面链接的代码:

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

// 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");
}
// https://github.com/TheDeveloper/redis-session-php
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>";

?>

I'm not very familiar with Redis or Node, but from looking at the code of the RedisSession class ( https://github.com/TheDeveloper/redis-session-php ), I'd say you'll have to go with a custom session handler. 我对Redis或Node不太熟悉,但是通过查看RedisSession类的代码( https://github.com/TheDeveloper/redis-session-php ),我会说您必须继续自定义会话处理程序。 Whether the session handler should fumble with cookies is highly debatable, I'd probably put that somewhere else in the bootstrapping process, maybe as a dispatcher filter. 会话处理程序是否应该使用cookie尚待商,,我可能会在引导过程中将其放在其他位置,也许作为调度程序过滤器。

However if you need the session ID to be in a specific format, then you'll also have to make use of a custom session class, at least unless you want to/can make use of the undocumented session id genereation handler stuff that was introduced with PHP 5.5.1. 但是,如果您需要将会话ID设置为特定格式,那么至少还必须使用自定义会话类,除非您希望/可以使用引入的未记录的会话ID生成处理程序的东西使用PHP 5.5.1。

Creating an extended session class that handles this is fairly easy, just overwrite the start() and renew() method and do whatever you need to do with the ID. 创建一个处理该问题的扩展会话类非常容易,只需覆盖start()renew()方法,然后对ID进行任何处理即可。

Injecting the new session class into the application is pretty easy, as throughout the framework the session is being retrieved from the request ( \\Cake\\Network\\Request::session() ). 将新的会话类注入应用程序非常容易,因为在整个框架中,都是从请求( \\Cake\\Network\\Request::session() )中检索\\Cake\\Network\\Request::session() However getting your custom class into the request is a little ugly, as there is no clean way to hook this into the process of creating a request from globals. 但是,将您的自定义类添加到请求中有点麻烦,因为没有干净的方法可以将其挂接到从全局变量创建请求的过程中。 In any case you'll have to modify your front controller ( webroot/index.php ) so that the proper(ly configured) request class is being passed to the dispatcher. 无论如何,您都必须修改前端控制器( webroot/index.php ),以便将正确(已配置)的请求类传递给调度程序。

You can either 你可以

  • create a custom request class with for example an overwritten Request::createFromGlobals() method where you'll instantiate your custom session class and pass it to the config 使用一个覆盖的Request::createFromGlobals()方法创建一个自定义请求类,您将在其中实例化自定义会话类并将其传递给config

  • instantiate a new request class manually where you could pass the session object to use using the session config key (this will require you to figure the base and webroot options yourself) 手动实例化一个新的请求类,您可以在其中传递会话对象以使用session配置键使用(这将需要您自己确定basewebroot选项)

  • or overwrite the already assigned/constructed session class with your custom one using the Request::session() method. 或使用Request::session()方法用您的自定义类覆盖已分配/构造的会话类。

See also 也可以看看

In this example I'll go with a custom request class, simply to avoid the additional session class instantiation. 在此示例中,我将使用自定义请求类,只是为了避免其他会话类实例化。

src/Network/MyCustomSession.php src / Network / MyCustomSession.php

namespace App\Network;

use Cake\Network\Session;

class MyCustomSession extends Session
{
    public function start()
    {
        parent::start();
        $this->_processSessionId();
    }

    public function renew()
    {
        parent::renew();
        $this->_processSessionId();
    }

    protected function _processSessionId()
    {
        $id = $this->id();

        // To make this less handler specific, you could for example
        // use a configurable callback instead, or maybe even an event,
        // in the end this is just example code.

        if($id && substr($id, 0, 2) !== 's:') {
            $hmac = str_replace(
                "=", "", base64_encode(hash_hmac('sha256', $id, \EXPRESS_SECRET, true))
            );
            $this->id("s:$id.$hmac");
        }
    }
}

src/Network/MyCustomRequest.php src / Network / MyCustomRequest.php

namespace App\Network;

use Cake\Network\Request;

class MyCustomRequest extends Request
{
    public static function createFromGlobals()
    {
        list($base, $webroot) = static::_base();
        $sessionConfig = (array)Configure::read('Session') + [
            'defaults' => 'php',
            'cookiePath' => $webroot
        ];
        $config = [
            'query' => $_GET,
            'post' => $_POST,
            'files' => $_FILES,
            'cookies' => $_COOKIE,
            'environment' => $_SERVER + $_ENV,
            'base' => $base,
            'webroot' => $webroot,

             // here comes the custom session
            'session' => MyCustomSession::create($sessionConfig)
        ];
        $config['url'] = static::_url($config);
        return new static($config);
    }
}

src/webroot/index.php src / webroot / index.php

use App\Network\MyCustomRequest;

$dispatcher = DispatcherFactory::create();
$dispatcher->dispatch(
    MyCustomRequest::createFromGlobals(), // there goes the custom request
    new Response()
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM