翼度科技»论坛 编程开发 PHP 查看内容

从0构建Oauth2Server服务 之Token 编解码

11

主题

11

帖子

33

积分

新手上路

Rank: 1

积分
33


Token 编解码

令牌提供了一种通过在令牌字符串本身中编码所有必要信息来避免将令牌存储在数据库中的方法。这样做的主要好处是 API 服务器能够验证访问令牌,而无需对每个 API 请求进行数据库查找,从而使 API 更容易扩展。
OAuth 2.0 Bearer Tokens 的好处是应用程序不需要知道您决定如何在您的服务中实现访问令牌。这意味着以后可以在不影响客户端的情况下更改您的实现。
如果您已经拥有一个可水平扩展的分布式数据库系统,那么您可能无法通过使用自编码令牌获得任何好处。事实上,如果您已经解决了分布式数据库问题,则使用自编码令牌只会引入新问题,因为使自编码令牌无效成为一个额外的障碍。
有很多方法可以对令牌进行自编码。您选择的实际方法只对您的实施很重要,因为令牌信息不会暴露给外部开发人员。
实现自编码令牌的最常见方法是使用 JWS 规范,创建要包含在令牌中的所有数据的 JSON 序列化表示,并使用只有授权服务器知道的私钥对生成的字符串进行签名.

JWT 访问令牌编码

下面的代码是用 PHP 编写的,并使用Firebase PHP-JWT库来编码和验证令牌。您需要包含该库才能运行示例代码实际上,授权服务器将有一个用于签署令牌的私钥,资源服务器将从授权服务器元数据中获取公钥以用于验证令牌。在这个例子中,我们每次都生成一个新的私钥,并在同一个脚本中验证令牌。实际上,您需要将私钥存储在某处以使用相同的密钥一致地签署令牌。
  1. <?php
  2. use \Firebase\JWT\JWT;
  3. # Generate a private key to sign the token.
  4. # The public key would need to be published at the authorization
  5. # server if a separate resource server needs to validate the JWT
  6. $private_key = openssl_pkey_new([
  7.   'digest_alg' => 'sha256',
  8.   'private_key_bits' => 1024,
  9.   'private_key_type' => OPENSSL_KEYTYPE_RSA
  10. ]);
  11. # Set the user ID of the user this token is for
  12. $user_id = "1000";
  13. # Set the client ID of the app that is generating this token
  14. $client_id = 'https://example-app.com';
  15. # Provide the list of scopes this token is valid for
  16. $scope = 'read write';
  17. $token_data = array(
  18.   # Issuer (the authorization server identifier)
  19.   'iss' => 'https://' . $_SERVER['PHP_SELF'],
  20.   # Expires At
  21.   'exp' => time()+7200, // Valid for 2 hours
  22.   # Audience (The identifier of the resource server)
  23.   'aud' => 'api://default',
  24.   # Subject (The user ID)
  25.   'sub' => $user_id,
  26.   # Client ID
  27.   'client_id' => $client_id,
  28.   # Issued At
  29.   'iat' => time(),
  30.   # Identifier of this token
  31.   'jti' => microtime(true).'.'.bin2hex(random_bytes(10)),
  32.   # The list of OAuth scopes this token includes
  33.   'scope' => $scope
  34. );
  35. $token_string = JWT::encode($token_data, $private_key, 'RS256');
复制代码
这将产生一个字符串,例如:
  1. eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2F1dGhvcml6YXRpb24tc2VydmVyLmNvbS8iLCJleHAiOjE2MzczNDQ1NzIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJzdWIiOiIxMDAwIiwiY2xpZW50X2lkIjoiaHR0cHM6Ly9leGFtcGxlLWFwcC5jb20iLCJpYXQiOjE2MzczMzczNzIsImp0aSI6IjE2MzczMzczNzIuMjA1MS42MjBmNWEzZGMwZWJhYTA5NzMxMiIsInNjb3BlIjoicmVhZCB3cml0ZSJ9.SKDO_Gu96WeHkR_Tv0d8gFQN1SEdpN8S_h0IJQyl_5syvpIRA5wno0VDFi34k5jbnaY5WHn6Y912IOmg6tMO91KlYOU1MNdVhHUoPoNUzYtl_nNab7Ywe29kxgrekm-67ZInDI8RHbSkL7Z_N9eZz_J8c3EolcsoIf-Dd5n9y_Y
复制代码
该令牌由三个部分组成,以句点分隔。第一部分描述了使用的签名方法。第二部分包含令牌数据。第三部分是签名。
例如,此令牌的第一个组件是此 JSON 对象:
  1. {
  2.    "typ":"JWT",
  3.    "alg":"RS256"
  4. }
复制代码
第二个组件包含 API 端点处理请求所需的实际数据,例如用户标识和范围访问。
  1. {
  2.   "iss": "https://authorization-server.com/",
  3.   "exp": 1637344572,
  4.   "aud": "api://default",
  5.   "sub": "1000",
  6.   "client_id": "https://example-app.com",
  7.   "iat": 1637337372,
  8.   "jti": "1637337372.2051.620f5a3dc0ebaa097312",
  9.   "scope": "read write"
  10. }
复制代码
然后对这两个部分进行 base64 编码,JWT 库计算这两个字符串的 RS256 签名,然后用句点连接所有三个部分。

解码

可以使用相同的 JWT 库验证访问令牌。该库将同时对签名进行解码和验证,如果签名无效或令牌的到期日期已过,则抛出异常。
您需要与签署令牌的私钥相对应的公钥。通常,您可以从授权服务器的元数据文档中获取它,但在本例中,我们将从之前生成的私钥中派生出公钥。
注意:任何人都可以通过对令牌字符串的中间部分进行base64解码来读取令牌信息。因此,不要在令牌中存储私人信息或您不希望用户或开发人员看到的信息,这一点很重要。如果想隐藏token信息,可以使用JSON Web Encryption spec对token中的数据进行加密。
  1. $public_key = openssl_pkey_get_details($private_key)['key'];
  2. try {
  3.   # Note: You must provide the list of supported algorithms in order to prevent
  4.   # an attacker from bypassing the signature verification. See:
  5.   # https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
  6.   $token = JWT::decode($token_string, $jwt_key, ['RS256']);
  7.   $error = false;
  8. } catch(\Firebase\JWT\ExpiredException $e) {
  9.   $token = false;
  10.   $error = 'expired';
  11.   $error_description = 'The token has expired';
  12. } catch(\Firebase\JWT\SignatureInvalidException $e) {
  13.   $token = false;
  14.   $error = 'invalid';
  15.   $error_description = 'The token provided was malformed';
  16. } catch(Exception $e) {
  17.   $token = false;
  18.   $error = 'unauthorized';
  19.   $error_description = $e->getMessage();
  20. }
  21. if($error) {
  22.   header('HTTP/1.1 401 Unauthorized');
  23.   echo json_encode(array(
  24.     'error'=>$error,
  25.     'error_description'=>$error_description
  26.   ));
  27.   die();
  28. } else {
  29.   // Now $token has all the data that we encoded in it originally
  30.   print_r($token);
  31. }       
复制代码
Invalidating

因为令牌可以在不进行数据库查找的情况下进行验证,所以在令牌过期之前无法使其失效。您需要采取额外的步骤来使自编码的令牌无效,例如临时存储已撤销令牌的列表,这是令
  1. jti
复制代码
牌中声明的一种用途。有关详细信息,请参阅刷新访问令牌。
以上就是从0构建Oauth2Server服务 之Token 编解码的详细内容,更多关于Oauth2Server Token编解码的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/article/283307.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具