0

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

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

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

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

请帮忙?

PS 我没有足够的声誉来创建标签“Latchet”,所以我使用的是 Ratchet(Latchet 是基于它构建的)。

4

3 回答 3

3

创建一个名为 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

于 2014-06-21T23:57:04.327 回答
1

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

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

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

于 2014-06-21T13:15:02.890 回答
0

好的,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 种方法:subscribepublish和。由于 autobahn 发出的 authreq 和 auth 调用都是 RPC 调用,它们由控制器上的方法处理。callunsubscribecall

该解决方案首先由 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 来作弊。

于 2014-06-24T21:04:58.020 回答