REST 作为一个概念的要点之一是避免使用会话状态,以便更轻松地水平扩展 REST 终结点的资源。如果您计划使用PHP,如问题中所述,您将发现自己处于一个困难的境地,即在您想要扩展的情况下必须实现共享会话存储。$_SESSION
虽然OAuth是您想要执行的操作的首选方法,但完整的实现可能比您想要投入的工作量更多。但是,您可以制定一些折衷措施,并且仍然保持无会话状态。您以前甚至可能见过类似的解决方案。
- 预配 API 帐户后,生成 2 个随机值:令牌和机密。
- 当客户端发出请求时,他们会提供:
- 令牌,以明文形式显示。
- 根据唯一但已知的值和机密计算的值。例如:HMAC或加密签名
- 然后,REST 终结点可以维护令牌和机密的简单、集中的键值存储,并通过计算值来验证请求。
通过这种方式,您可以保持“无会话”REST理想,并且在交换的任何部分都不会实际传输秘密。
客户端示例:
$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);
值得注意的是,如果决定使用时间戳作为随机数,您应该只接受过去几分钟内生成的时间戳,以防止重放攻击。大多数其他身份验证方案将在签名数据中包含其他组件,例如资源路径、标头数据子集等,以进一步锁定签名以仅应用于单个请求。
当这个答案最初写于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"
并且可以由任何遵守该标准的人进行验证,这是非常受欢迎的atm。
无论如何,大多数 API 将它们附加到标头中,如下所示:
$request->addHeader("Authorization: Bearer $jwt");