php - 为 PHP REST API 实现简单的身份验证

标签 php rest api session authentication

关闭。这个问题是opinion-based .它目前不接受答案。












想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题.

3年前关闭。




Improve this question




我正在向旧版 PHP 站点添加 REST API。这是为内部应用程序提供一个端点,所以我在如何设计事物以及支持和不支持的方面非常自由。

我现在需要添加到此 API 的是一种登录方式,然后以特定用户身份执行操作。该网站是多年前建立的,不一定采用当时的最佳实践,因此不幸的是,我在如何做到这一点方面受到了一些限制。所有这些都需要在 PHP 5.4 和 MySQL 5.6 中运行。

我一直在阅读有关此的常见设计,OAuth1/2 看起来是最常见的标准。但是,对于我的目的来说,这似乎是一种过度杀伤,因为它具有我不需要的各种功能,而且实现起来似乎非常复杂。

相反,我打算做这样的事情:

  • 客户端调用 get_session API 端点会生成随机 session ID,将其保存到数据库中的表中并将其返回给客户端。
  • 客户端保存此 session ID。
  • 然后客户端通过向 login 发送请求来进行身份验证。端点,发送用户名、密码和 session ID(显然是通过 HTTPS)。
  • 服务器将数据与用户表进行比较,如果登录正确,则更新 session 表以将 session ID 与相应的用户 ID 相关联。这需要以某种方式限制速率以防止暴力破解。
  • 现在,客户端可以调用仅提供其 session ID 进行授权的任何其他端点。
  • 对于每个请求,服务器查找 session ID,查看它与哪个用户关联并执行正确的操作。
  • 客户端可以记住 session ID 以备将来使用,直到它被手动删除或在一段时间后过期。
  • 要注销,客户端向 logout 发送请求端点和服务器删除与用户帐户的关联。

  • 这是一个合理的设计吗?它显然不是很复杂,但我正在寻找可以在没有巨大麻烦或不需要第三方库的情况下实现的东西。

    最佳答案

    REST 作为一个概念的主要观点之一是避免使用 session 状态,以便更轻松地水平扩展 REST 端点的资源。如果您打算使用 PHP 的 $_SESSION如您的问题中所述,您会发现自己处于一个困难的境地,在您想要扩展的情况下必须实现共享 session 存储。

    虽然 OAuth 将是您想要执行的操作的首选方法,但完整的实现可能比您想投入的工作要多。但是,您可以制定一些半措施,并且仍然保持无 session 。您以前甚至可能见过类似的解决方案。

  • 配置 API 帐户时,生成 2 个随机值: token 和 secret 。
  • 当客户提出请求时,他们提供:
  • token ,以明文形式。
  • 根据唯一但已知的值和 Secret 计算出的值。例如:HMAC 或加密签名
  • 然后,REST 端点可以维护一个简单、集中的 Tokens 和 Secrets 键值存储,并通过计算值来验证请求。

  • 通过这种方式,您可以保持“无 session ”REST 的理想状态,而且您在交换的任何部分都不会真正传输 Secret。

    客户端示例:
    $token  = "Bmn0c8rQDJoGTibk";                 // base64_encode(random_bytes(12));
    $secret = "yXWczx0LwgKInpMFfgh0gCYCA8EKbOnw"; // base64_encode(random_bytes(24));
    $stamp  = "2017-10-12T23:54:50+00:00";        // date("c");
    $sig    = hash_hmac('SHA256', $stamp, base64_decode($secret));
    // Result: "1f3ff7b1165b36a18dd9d4c32a733b15c22f63f34283df7bd7de65a690cc6f21"
    
    $request->addHeader("X-Auth-Token: $token");
    $request->addHeader("X-Auth-Signature: $sig");
    $request->addHeader("X-Auth-Timestamp: $stamp");
    

    服务器示例:
    $token  = $request->getToken();
    $secret = $auth->getSecret($token);
    $sig    = $request->getSignature();
    
    $success = $auth->validateSignature($sig, $secret);
    

    值得注意的是,如果决定使用时间戳作为随机数,您应该只接受最近几分钟内生成的时间戳,以防止重放攻击。大多数其他身份验证方案将在签名数据中包含其他组件,例如资源路径、 header 数据的子集等,以进一步锁定签名以仅适用于单个请求。

    2020 编辑:这基本上就是 JSON Web Tokens [JWT]是。

    当这个答案最初是在 2013 年编写时,JWT 还很新,[而且我没有听说过它们],但到 2020 年,它们已经牢固地确立了它们的实用性。下面是一个手动实现的例子来说明它们的简单性,但是有大量的库可以为您进行编码/解码/验证,可能已经融入您选择的框架中。
    function base64url_encode($data) {
      $b64 = base64_encode($data);
      if ($b64 === false) {
        return false;
      }
      $url = strtr($b64, '+/', '-_');
      return rtrim($url, '=');
    }
    
    $token  = "Bmn0c8rQDJoGTibk";                 // base64_encode(random_bytes(12));
    $secret = "yXWczx0LwgKInpMFfgh0gCYCA8EKbOnw"; // base64_encode(random_bytes(24));
    
    // RFC-defined structure
    $header = [
        "alg" => "HS256",
        "typ" => "JWT"
    ];
    
    // whatever you want
    $payload = [
        "token" => $token,
        "stamp" => "2020-01-02T22:00:00+00:00"    // date("c")
    ];
    
    $jwt = sprintf(
        "%s.%s",
        base64url_encode(json_encode($header)),
        base64url_encode(json_encode($payload))
    );
    
    $jwt = sprintf(
        "%s.%s",
        $jwt,
        base64url_encode(hash_hmac('SHA256', $jwt, base64_decode($secret), true))
    );
    
    var_dump($jwt);
    

    产量:
    string(167) "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6IkJtbjBjOHJRREpvR1RpYmsiLCJzdGFtcCI6IjIwMjAtMDEtMDJUMjI6MDA6MDArMDA6MDAifQ.8kvuFR5xgvaTlOAzsshymHsJ9eRBVe-RE5qk1an_M_w"
    

    并且可以是validated by anyone符合标准,这是非常受欢迎的atm。

    无论如何,大多数 API 将它们添加到 header 中:
    $request->addHeader("Authorization: Bearer $jwt");
    

    关于php - 为 PHP REST API 实现简单的身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46719676/

    相关文章:

    php - PHP 的日志流过滤器?

    PHP/mySQL - 在 'CONCAT' 子句中使用 'AS' 和 'LIKE' 的结果

    javascript - 以PHP形式以表格方式显示数据

    angularjs - Angular 和 CORS

    javascript - Stripe - 通过 1 个 HTML 按钮提交 token /创建费用?

    javascript - Vuex - 调用异步函数

    javascript - 谷歌地图错误 "' addDomListener' of undefined"

    php - 允许 $wpdb mysql 查询处理特定的 WordPress 类别

    rest - 请求全部排除属性的资源列表(例如外连接)的常见 REST 方式是什么

    django - 如果内容在缓存 TTL 到期之前发生更改,则更新缓存