【问题标题】:Building realtime app using Laravel and Latchet websocket使用 Laravel 和 Latchet websocket 构建实时应用程序
【发布时间】:2014-06-21 07:19:28
【问题描述】:

我正在构建一个封闭的应用程序(用户需要进行身份验证才能使用它)。我无法从我的 Latchet 会话中识别当前经过身份验证的用户。由于 apache 不支持长期连接,我将 Latchet 托管在单独的服务器实例上。这意味着我的用户会收到两个 session_id。每个连接一个。我希望能够识别两个连接的当前用户。

我的客户端代码是一个基于 AngularJS 的 SPA。对于客户端 WS,我使用的是 Autobahn.ws WAMP v1 实现。 ab 框架指定了身份验证方法:http://autobahn.ws/js/reference_wampv1.html#session-authentication,但我究竟该怎么做呢?

我是否将用户名和密码保存在客户端并在执行登录后重新传输(顺便说一句,这与我的 SPA 的其余部分是分开的)?如果是这样,这不是一个安全问题吗?

服务器端会收到什么身份验证请求?我找不到任何这样的例子......

请帮忙?

附:我没有足够的声誉来创建标签“Latchet”,所以我改用 Ratchet(Latchet 是在它的基础上构建的)。

【问题讨论】:

    标签: angularjs laravel websocket autobahn ratchet


    【解决方案1】:

    创建一个名为 AuthenticationService 的 angularjs 服务,在需要的地方注入并调用它:

    AuthenticationService.check('login_name', 'password');
    

    此代码存在于名为 authentication.js 的文件中。它假定高速公路已经包含在内。我确实必须大量编辑这段代码,删除其中所有多余的废话,它可能有一两个语法错误,但想法就在那里。

    angular.module(
      'top.authentication',
      ['top']
    )
    
    .factory('AuthenticationService', [ '$rootScope', function($rootScope) {
        return {
            check: function(aname, apwd) {
                  console.log("here in the check function");
                  $rootScope.loginInfo = { channel: aname, secret: apwd };
                  var wsuri = 'wss://' + '192.168.1.11' + ':9000/';
                  $rootScope.loginInfo.wsuri = wsuri;
                  ab.connect(wsuri,
                      function(session) {
                          $rootScope.loginInfo.session = session;
                          console.log("connected to " + wsuri);
                          onConnect(session);
                      },
                      function(code,reason) {
                          $rootScope.loginInfo.session = null;
                          if ( code == ab.CONNECTION_UNSUPPORTED) {
                              console.log(reason);
                          } else {
                              console.log('failed');
                              $rootScope.isLoggedIn = 'false';
                          }
                      }
                  );
    
                  function onConnect(sess) {
                      console.log('onConnect');
                      var wi = $rootScope.loginInfo;
                      sess.authreq(wi.channel).then(
                        function(challenge) {
                            console.log("onConnect().then()");
                            var secret = ab.deriveKey(wi.secret,JSON.parse(challenge).authextra);
                            var signature = sess.authsign(challenge, secret);
                            sess.auth(signature).then(onAuth, ab.log);
                        },ab.log
                      );
                  }
    
                  function onAuth(permission) {
                      $rootScope.isLoggedIn = 'true';
                      console.log("authentication complete");
                      // do whatever you need when you are logged in..
                  }
            }
        };
        }])
    

    那么您需要服务器端的代码(如您所指出的)。我假设您的服务器端 Web 套接字是 php 编码。我对此无能为力,已经一年多没有用 php 编码了。在我的例子中,我使用 python,我包含了高速公路设备,然后是 WampCraServerProtocol 的子类,并替换了一些方法(onSessionOpen、getAuthPermissions、getAuthSecret、onAuthenticated 和 onClose)正如你所想象的,这些是敲门的角码。我认为高速公路不支持 php,因此,您必须自己编写身份验证的服务器端。

    无论如何,我的后端更像@oberstat 所描述的那样工作。我通过旧式 https 建立身份验证,创建会话 cookie,然后执行 ajax 请求“票证”(这是我与 Web 身份验证会话相关联的临时名称/密码)。这是一个一次性的名称/密码,必须在几秒钟内使用,否则它会消失。关键是我不必保留用户的凭据,我已经有了 cookie/会话,我可以创建可以使用的票证。这也有一个很好的副作用,我的 ajax 会话与我的 Web 套接字会话相关,对任何一个的查询都归因于后端的同一会话。

    -g

    【讨论】:

    【解决方案2】:

    我可以给你一些关于 WAMP-CRA 的提示,这就是这里所指的身份验证机制:

    WAMP-CRA 不会通过网络发送密码。它通过挑战-响应方案工作。客户端和服务器有一个共享的秘密。为了对客户端进行身份验证,服务器将发送一个挑战(随机的)客户端需要签名 - 使用密钥。并且只有签名被发回。客户端可能会将机密存储在浏览器本地存储中。它从未发送过。

    在上面的一个变体中,服务器发送的挑战的签名不是直接在客户端内签名的,但是客户端可能会让签名从 Ajax 请求中创建。当客户端已经使用其他方式(例如基于经典 cookie)进行身份验证时,这很有用,然后可以在进行身份验证的经典 Web 应用程序中完成签名。

    【讨论】:

    • 我不太确定这应该如何工作。用户名/密码是质询/响应身份验证。在 ws 服务器和客户端之间,唯一知道任何身份的一方是客户端。客户端持有一个会话 cookie,我可以将其用作秘密,但我无法在 Laravel 中查询具有特定 ID 的会话,可以吗?
    【解决方案3】:

    好的,Greg 非常友好地提供了一个完整的客户端实现示例,所以我不会再做任何事情了。它只需对我能想到的几乎所有用例进行一些调整和修改即可工作。我会将他的答案标记为正确答案。但他的意见只涵盖了后端实现的理论,所以我将尝试在这里填补空白以备后验。

    我必须指出,这里的解决方案并不完整,因为它没有在我的 SPA/REST 连接和我的 WS 连接之间提供共享会话。

    我发现高速公路传输的身份验证请求实际上是 RPC 的变体,并且出于某种原因硬编码的主题名称奇怪地类似于常规 url:

    - 'http://api.wamp.ws/procedure#authreq' - for auth requests
    - 'http://api.wamp.ws/procedure#auth'    - for signed auth client responses
    

    我需要在我的 Laravel routes.php 中再创建两条路由

    // WS CRA routes
    Latchet::topic('http://api.wamp.ws/procedure#authreq', 'app\\socket\\AuthReqController');
    Latchet::topic('http://api.wamp.ws/procedure#auth',    'app\\socket\\AuthReqController');
    

    现在,Latchet 控制器有 4 种方法:subscribepublishcallunsubscribe。由于 autobahn 发出的 authreq 和 auth 调用都是 RPC 调用,因此它们由控制器上的 call 方法处理。

    首先由 oberstet 提出然后由 Greg 支持的解决方案描述了根据请求生成的临时身份验证密钥和秘密,并临时保留足够长的时间以供 WS CRA 程序验证。因此,我创建了一个生成持久键值对的 REST 端点。此处不包括端点,因为我确信这是微不足道的。

    class AuthReqController extends BaseTopic {
        public function subscribe ($connection, $topic) {    }
    
        public function publish ($connection, $topic, $message, array $exclude, array $eligible) {    }
    
        public function unsubscribe ($connection, $topic) {    }
    
        public function call ($connection, $id, $topic, array $params) {
            switch ($topic) {
                case 'http://api.wamp.ws/procedure#authreq':
                    return $this->getAuthenticationRequest($connection, $id, $topic, $params);
                case 'http://api.wamp.ws/procedure#auth':
                    return $this->processAuthSignature($connection, $id, $topic, $params);
            }
        }
    
        /**
         * Process the authentication request
         */
        private function getAuthenticationRequest ($connection, $id, $topic, $params) {
            $auth_key = $params[0]; // A generated temporary auth key
            $tmpUser  = $this->getTempUser($auth_key); // Get the key value pair as persisted from the temporary store.
            if ($tmpUser) {
                $info = [
                    'authkey'   => $tmpUser->username,
                    'secret'    => $tmpUser->secret,
                    'timestamp' => time()
                ];
                $connection->callResult($id, $info); 
            } else {
                $connection->callError($id, $topic, array('User not found'));
            }
            return true;
        }
    
        /**
         * Process the final step in the authentication
         */
        private function processAuthSignature ($connection, $id, $topic, $params) {
            // This should do something smart to validate this response.
    
            // The session should be ours right now. So store the Auth::user()
            $connection->user = Auth::user(); // A null object is stored.
            $connection->callResult($id, array('msg' => 'connected'));
        }
    
        private function getTempUser($auth_key) {
            return TempAuth::findOrFail($auth_key);
        }
    }
    

    现在这里的某个地方我出错了。因为如果我应该继承我的应用程序持有的 ajax 会话,我将能够从任何其他基于 WS Latchet 的控制器调用 Auth::user() 并自动显示当前登录的用户。但这种情况并非如此。因此,如果有人看到我做错了什么,请大声疾呼。请!

    由于我无法获得共享会话,我目前正在通过将真实用户名作为 RPC 调用而不是执行完整的 CRA 来进行作弊。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      • 2019-07-24
      • 2017-10-03
      • 1970-01-01
      • 2019-11-07
      • 2020-11-22
      • 2022-08-03
      相关资源
      最近更新 更多