验证Parse.com用户和会话服务器端

时间:2015-07-05 17:08:48

标签: javascript rest parse-platform

我目前正在通过使用Parse Javascript SDK构建一些示例客户端应用来探索Parse平台。我决定将大部分与Parse相关的逻辑从应用程序和VPS上运行的RESTful API中抽象出来。目的是使应用程序尽可能地作为后端不可知。

我构建的RESTful API只是一个使用Node.js Parse包的Express.js应用程序。

我的问题是:当我想向REST API发出请求时,验证发出请求的用户的最佳方法是什么?

示例:考虑用户可以拥有朋友的应用:

  1. 用户使用Parse.User.logIn()方法登录客户端。
  2. 客户端点击我的REST API上的端点/用户/我/朋友,该API检查用户是否具有有效会话并返回用户朋友列表。
  3. 所以这需要服务器端的Parse代码来确定:

    1. 提出请求的用户是
    2. 如果当前用户具有有效会话
    3. 我考虑过使用以下方法:

      1. 当用户登录客户端时,他/她会收到会话令牌。
      2. 在发出API请求时将此会话令牌作为标头附加。
      3. 在服务器上,将此会话令牌与Parse.User.become()方法结合使用,以设置当前用户并执行所需的逻辑:
      4. 因此快递应用程序将处理上述示例:
      5. router.get("/users/me/friends", function(req, res) {
            var sessionToken = req.headers["sessionToken"];
            Parse.User.become(sessionToken).then(function(user) {
                /* Valid user and session: find and return the users friends */
            }, function(error) {
                /* Invalid: return an error status and the error */
            });
        });
        

        这是处理这种情况的有效方法吗?如果我继续这样,是否有任何我应该注意的问题?谢谢!

1 个答案:

答案 0 :(得分:2)

我一直在问自己同样的问题,我提出的解决方案与你解释的非常相似。我正在开发一个使用Parse的Android应用程序,因为它的用户和用户只有会话部分,其他一切都将是MySQL,Redis或我计划在未来包装的其他数据源。我已经使用PHP和Slim Framework在VPS上开发了自己的Rest API。我通过在我自己生成的users表中添加ApiKey列,然后利用已为每个用户创建和管理的SessionToken,在Parse中使用了身份验证部分。当用户对我的Web服务进行经过身份验证的调用时,它们会在每个请求标头中提供api密钥和会话令牌,当我的服务器收到请求时,通过Parse验证apiKey + sessionToken对是否有效。每次用户重新登录时都会重新创建sessionToken,因此这会使其在一定程度上保持安全。所以目前我正在使用Parse Android SDK客户端和Parse PHP SDK服务器端。我已经能够创建关于解析的创建用户,登录用户和列表用户端点。您提到的“成为”能力确实帮助我解决了用户将会话令牌发送到服务器并尝试验证它的问题,因为只有当前用户才能查看自己的会话信息。我在PHP文档中找不到任何提及它,但显然它可用,你只需要做类似下面的事情。如果您有任何想法或意见可以使这更好,我愿意接受。

DBHandlerParse类摘录:

   /**
     * Validating user api key
     * If the api key is there in db, it is a valid key
     * @param String $api_key user api key
     * @return boolean
     */
    public function isValidApiKey($api_key) {
        $query = ParseUser::query();
        $query->equalTo("apiKey",$api_key);
        $results = $query->find();

        if(count($results) > 0){
            return true;
        } else{
            return false;
        }
    }

    /**
     * Validating user session token
     * If the session token matches the api key user, it is a valid token
     * @param String $session_token user session token
     * @param String $api_key user api key
     * @return boolean
     */
    public function isValidSessionToken($session_token, $api_key) {

        // Using already validated $api_key, obtain corresponding user object
        $query = ParseUser::query();
        $query->equalTo("apiKey",$api_key);
        $results = $query->find();

        if(count($results) > 0){
            $userObj = $results[0];
        } else{
            return FALSE;
        }

        try{
            // Become user that has this session token
            // Only way to query back the user that they are
            // If no user is found with this token, parse error
            $thisUser = ParseUser::become($session_token);

            $query = ParseSession::query();
            $query->equalTo("user", $userObj);
            $results = $query->find();

            if(count($results) > 0){
                return TRUE;
            } else{
                return FALSE;
            }
        } catch (Parse\ParseException $error){
            return FALSE;
        }
    }

身份验证中间件:

/**
 * Adding Middle Layer to authenticate every request
 * Checking if the request has valid api key in the 'Authorization' & 'Token' header
 */
function authenticate(\Slim\Route $route) {
    // Getting request headers
    $headers = apache_request_headers();
    $response = array();
    $app = \Slim\Slim::getInstance();

    // Verifying Authorization Header
    if (isset($headers['Authorization']) && isset($headers['Token'])) {
        $db = new DbHandlerParse();

        // get the api key
        $api_key = $headers['Authorization'];
        // get the session token
        $session_token = $headers['Token'];

        // validating api key
        if (!$db->isValidApiKey($api_key)) {
            // api key is not present in users table
            $response["result"] = "error";
            $response["message"] = "Access Denied. Invalid Api key";
            echoRespnse(401, $response);
            $app->stop();
        } else if(!$db->isValidSessionToken($session_token, $api_key)) {
            // session token does not match api key or is just invalid
            $response["result"] = "error";
            $response["message"] = "Access Denied. Invalid Token";
            echoRespnse(401, $response);
            $app->stop();
        } else {
            global $user_id;
            // get user primary key id
            $userID = $db->getUserId($api_key);
            if (NULL != $userID)
                $user_id = $userID;
        }
    } else if(!isset($headers['Authorization'])){
        // api key is missing in header
        $response["result"] = "error";
        $response["message"] = "Api key is misssing";
        echoRespnse(400, $response);
        $app->stop();
    } else {
        // token is missing in header
        $response["result"] = "error";
        $response["message"] = "Token is misssing";
        echoRespnse(400, $response);
        $app->stop();
    }
}

示例路线:

/**
 * Users List
 * url - /list
 * method - GET
 */
$app->get('/users/list', 'authenticate', function() use ($app) {
    $response = array();

    $db = new DbHandlerParse();
    $results = $db->getUserList();
    $records = array();

    //echo "Successfully retrieved " . count($results) . " scores.<br><br>";
    // Do something with the returned ParseObject values
    for ($i = 0; $i < count($results); $i++) {
        $object = $results[$i];

        $record = array();
        $records[$i]['userId'] = $object->getObjectId();
        $records[$i]['firstName'] = $object->get('firstName');
        $records[$i]['lastName'] = $object->get('lastName');
        $records[$i]['username'] = $object->get('username');
        $records[$i]['email'] = $object->get('email');

        //echo $object->getObjectId() . ' - ' . $object->get('username') . '<br>';
    }

    // check for records returned
    if ($records) {
        $response["result"] = "success";
        $response['message'] = count($records)." users found.";
        $response['items'] = $records;

    } else {
        // no records found
        $response['result'] = 'success';
        $response['message'] = 'No Users Found';
    }

    echoRespnse(200, $response);
});