CakePhp 3.x使用redis与nodejs共享会话

时间:2015-06-25 02:16:02

标签: node.js redis cakephp-3.0

好吧,我一直在关注使用redis在php和nodejs之间共享会话的示例:https://gist.github.com/mscdex/9507b0d8df42e0aec825

我的工作非常漂亮,但现在我正试着看看我将如何使用CakePhp。我有几个问题我会怎么做:

  1. 我应该只创建一个新的会话处理程序并在构造函数内运行会话开始之前我需要做的会话配置吗?

  2. 我应该创建一个从CakePhp提供的扩展的新Session.php类吗?如果我这样做,我将如何让应用程序使用它?

  3. 上面链接的代码:

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

1 个答案:

答案 0 :(得分:1)

我对Redis或Node不是很熟悉,但是通过查看start()类的代码( https://github.com/TheDeveloper/redis-session-php ),我会说你会必须使用自定义会话处理程序。会话处理程序是否应该与cookie混淆是非常值得商榷的,我可能会把它放在引导过程中的其他地方,可能作为调度程序过滤器。

但是,如果您需要会话ID采用特定格式,那么您还必须使用自定义会话类,至少除非您希望/可以使用未记录的session id genereation handler stuff这是PHP 5.5.1引入的。

创建一个处理此问题的扩展会话类非常简单,只需覆盖renew()\Cake\Network\Request::session()方法,并执行您需要对ID执行的任何操作。

将新会话类注入应用程序非常简单,因为整个框架中的会话都是从请求中检索的(webroot/index.php)。但是将自定义类放入请求中有点难看,因为没有干净的方法将其挂钩到从全局变量创建请求的过程中。在任何情况下,您都必须修改前端控制器(Request::createFromGlobals()),以便将正确的(已配置的)请求类传递给调度程序。

你可以

  • 使用例如覆盖的session方法创建自定义请求类,您将实例化自定义会话类并将其传递给配置

  • 手动实例化一个新的请求类,您可以使用base配置密钥传递会话对象(这需要您考虑webrootRequest::session()选项自己)

  • 或使用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 /网络/ MyCustomSession.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 /网络/ MyCustomRequest.php

use App\Network\MyCustomRequest;

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

<强>的src /根目录/ index.php的

angular.module('myModule')
.directive('myDirective', [ function () 
{
    'use strict';
    return {
        restrict: 'E',
        templateUrl: '/static/quality/scripts/directives/hh-star-rating.html',
        scope: {
            varA:'@',
        },
        controller: [
            '$scope', '$controller',
            function ($scope, $controller) {
                $controller('otherController', {$scope: $scope})
                    .columns(
                        $scope.x,
                        $scope.y,
                        $scope.series
                    );
                    $scope.transformData =  function(data)
                    {
                        /// does something;

                        return data;
                    };
            }
        ],