首页 > 其他分享 >从0开始构建一个Oauth2Server服务 <19> Token 编解码

从0开始构建一个Oauth2Server服务 <19> Token 编解码

时间:2023-05-03 19:31:40浏览次数:51  
标签:编解码 令牌 私钥 编码 JWT token Oauth2Server Token https

Token 编解码

从0开始构建一个Oauth2Server服务 <19> Token 编解码 _ide

令牌提供了一种通过在令牌字符串本身中编码所有必要信息来避免将令牌存储在数据库中的方法。这样做的主要好处是 API 服务器能够验证访问令牌,而无需对每个 API 请求进行数据库查找,从而使 API 更容易扩展。

OAuth 2.0 Bearer Tokens 的好处是应用程序不需要知道您决定如何在您的服务中实现访问令牌。这意味着以后可以在不影响客户端的情况下更改您的实现。

如果您已经拥有一个可水平扩展的分布式数据库系统,那么您可能无法通过使用自编码令牌获得任何好处。事实上,如果您已经解决了分布式数据库问题,则使用自编码令牌只会引入新问题,因为使自编码令牌无效成为一个额外的障碍。

有很多方法可以对令牌进行自编码。您选择的实际方法只对您的实施很重要,因为令牌信息不会暴露给外部开发人员。

实现自编码令牌的最常见方法是使用 JWS 规范,创建要包含在令牌中的所有数据的 JSON 序列化表示,并使用只有授权服务器知道的私钥对生成的字符串进行签名.

JWT 访问令牌编码

从0开始构建一个Oauth2Server服务 <19> Token 编解码 _服务器_02

下面的代码是用 PHP 编写的,并使用Firebase PHP-JWT库来编码和验证令牌。您需要包含该库才能运行示例代码实际上,授权服务器将有一个用于签署令牌的私钥,资源服务器将从授权服务器元数据中获取公钥以用于验证令牌。在这个例子中,我们每次都生成一个新的私钥,并在同一个脚本中验证令牌。实际上,您需要将私钥存储在某处以使用相同的密钥一致地签署令牌。

<?php
use \Firebase\JWT\JWT;
 
# Generate a private key to sign the token.
# The public key would need to be published at the authorization
# server if a separate resource server needs to validate the JWT
 
$private_key = openssl_pkey_new([
  'digest_alg' => 'sha256',
  'private_key_bits' => 1024,
  'private_key_type' => OPENSSL_KEYTYPE_RSA
]);
 
# Set the user ID of the user this token is for
$user_id = "1000";
 
# Set the client ID of the app that is generating this token
$client_id = 'https://example-app.com';
 
# Provide the list of scopes this token is valid for
$scope = 'read write';
 
$token_data = array(
 
  # Issuer (the authorization server identifier)
  'iss' => 'https://' . $_SERVER['PHP_SELF'],
 
  # Expires At
  'exp' => time()+7200, // Valid for 2 hours
 
  # Audience (The identifier of the resource server)
  'aud' => 'api://default',
 
  # Subject (The user ID)
  'sub' => $user_id,
 
  # Client ID
  'client_id' => $client_id,
 
  # Issued At
  'iat' => time(),
 
  # Identifier of this token
  'jti' => microtime(true).'.'.bin2hex(random_bytes(10)),
 
  # The list of OAuth scopes this token includes
  'scope' => $scope
);
$token_string = JWT::encode($token_data, $private_key, 'RS256');

这将产生一个字符串,例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodH
RwczovL2F1dGhvcml6YXRpb24tc2VydmVyLmNvbS8iLCJleHAiO
jE2MzczNDQ1NzIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJzdWIi
OiIxMDAwIiwiY2xpZW50X2lkIjoiaHR0cHM6Ly9leGFtcGxlLWF
wcC5jb20iLCJpYXQiOjE2MzczMzczNzIsImp0aSI6IjE2MzczMz
czNzIuMjA1MS42MjBmNWEzZGMwZWJhYTA5NzMxMiIsInNjb3BlI
joicmVhZCB3cml0ZSJ9.SKDO_Gu96WeHkR_Tv0d8gFQN1SEdpN8
S_h0IJQyl_5syvpIRA5wno0VDFi34k5jbnaY5WHn6Y912IOmg6t
MO91KlYOU1MNdVhHUoPoNUzYtl_nNab7Ywe29kxgrekm-67ZInD
I8RHbSkL7Z_N9eZz_J8c3EolcsoIf-Dd5n9y_Y

该令牌由三个部分组成,以句点分隔。第一部分描述了使用的签名方法。第二部分包含令牌数据。第三部分是签名。

例如,此令牌的第一个组件是此 JSON 对象:

{
   "typ":"JWT",
   "alg":"RS256"
 }

第二个组件包含 API 端点处理请求所需的实际数据,例如用户标识和范围访问。

{
  "iss": "https://authorization-server.com/",
  "exp": 1637344572,
  "aud": "api://default",
  "sub": "1000",
  "client_id": "https://example-app.com",
  "iat": 1637337372,
  "jti": "1637337372.2051.620f5a3dc0ebaa097312",
  "scope": "read write"
}

然后对这两个部分进行 base64 编码,JWT 库计算这两个字符串的 RS256 签名,然后用句点连接所有三个部分。

解码

可以使用相同的 JWT 库验证访问令牌。该库将同时对签名进行解码和验证,如果签名无效或令牌的到期日期已过,则抛出异常。

您需要与签署令牌的私钥相对应的公钥。通常,您可以从授权服务器的元数据文档中获取它,但在本例中,我们将从之前生成的私钥中派生出公钥。

注意:任何人都可以通过对令牌字符串的中间部分进行base64解码来读取令牌信息。因此,不要在令牌中存储私人信息或您不希望用户或开发人员看到的信息,这一点很重要。如果想隐藏token信息,可以使用JSON Web Encryption spec对token中的数据进行加密。

<?php
$public_key = openssl_pkey_get_details($private_key)['key'];
 
try {
  # Note: You must provide the list of supported algorithms in order to prevent 
  # an attacker from bypassing the signature verification. See:
  # https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
  $token = JWT::decode($token_string, $jwt_key, ['RS256']);
  $error = false;
} catch(\Firebase\JWT\ExpiredException $e) {
  $token = false;
  $error = 'expired';
  $error_description = 'The token has expired';
} catch(\Firebase\JWT\SignatureInvalidException $e) {
  $token = false;
  $error = 'invalid';
  $error_description = 'The token provided was malformed';
} catch(Exception $e) {
  $token = false;
  $error = 'unauthorized';
  $error_description = $e->getMessage();
}
 
if($error) {
  header('HTTP/1.1 401 Unauthorized');
  echo json_encode(array(
    'error'=>$error, 
    'error_description'=>$error_description
  ));
  die();
} else {
  // Now $token has all the data that we encoded in it originally
  print_r($token);
}

Invalidating

因为令牌可以在不进行数据库查找的情况下进行验证,所以在令牌过期之前无法使其失效。您需要采取额外的步骤来使自编码的令牌无效,例如临时存储已撤销令牌的列表,这是令jti牌中声明的一种用途。有关详细信息,请参阅刷新访问令牌。

标签:编解码,令牌,私钥,编码,JWT,token,Oauth2Server,Token,https
From: https://blog.51cto.com/demo007x/6241526

相关文章

  • 彻底理解 cookie、session、token
    发展史1、很久很久以前,Web基本上就是文档的浏览而已,既然是浏览,作为服务器,不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议,就是请求加响应,尤其是我不用记住是谁刚刚发了HTTP请求,每个请求对我来说都是全新的。这段时间很嗨皮。2、但是随着交互式We......
  • CancellationToken 取消机制
    1、背景在我们访问一个十分耗时的请求的时候(这里以web请求举例)如果请求执行到一半的时候,用户把页面关掉了,那后台还在执行请求就会造成资源的浪费,所以需要引入取消请求机制2、举例代码很简单,直接居个小例子[HttpGet("GetFile")]publicasync......
  • 从0开始构建一个Oauth2Server服务 <15> 安全问题
    安全问题以下是构建授权服务器时应考虑的一些已知问题。网络钓鱼Attack针对OAuth服务器的一种潜在Attack是网络钓鱼Attack。这是Attack者创建一个看起来与服务授权页面相同的网页的地方,该页面通常包含用户名和密码字段。然后,Accacker可以通过各种手段诱骗用户访问该页面。除非用......
  • 【工具软件】Postman 设置登陆全局 token
    先设置Tests脚本constres=pm.response.json()pm.globals.set('token',res.data.token)使用......
  • js url urlencoding,乱码,编码,解码,编解码
    %25%37%DD上述形式不是乱码。这是urlencoding。可以使用js内置的方法encodeURIComponent进行编码再使用decodeURIComponent把上述形式再解码为普通字符对付火狐的自动编码有特效黑色头发:http://heisetoufa.iteye.com/......
  • npm安装yarn报npm ERR Unexpected token ‘.‘错误的解决办法
    问题背景最近发现很多前端框架或前端产品的学习和使用都用到了yarn,一开始不知道这是干什么的,后在网上搜索说是JS包管理工具,我一致都是用的nodeJs,难道node过时了,然后有一顿搜索,网上是这么介绍的。Yarn是什么?“Yarn是由Facebook、Google、Exponent和Tilde联合推出了一个新的JS......
  • Codeforces Round 847 (Div. 3) G.Tokens on Graph (构造)
    传送门题目大意  给定一个无向图,我们定义图中有四种点,一种是黑色的点表示终点,并且黑色的点始终是1号点,一种是红色的点,一种是灰色的点,最后一种就是没有颜色的点。  游戏规则:我们必须移动任意一个灰色的点到相邻的点上,如果灰色的点移动到了红色的点上,那么我们可以移动其他灰......
  • 出现错误expected token '<' (
    expectedtoken'<'(这个错误通常是因为您的代码所在的环境无法正确解析JSX语法。JSX是React框架中常用的语法扩展,用于在JavaScript中编写类似于HTML的代码。然而,JSX语法需要通过编译器或转换器才能被解析成纯JavaScript代码,因此需要一些额外的配置和工具才能在项目中使用。要......
  • 语义分割专栏(二)复习FCN的编解码结构
    前言 在这一期中,我们先简要复习一遍FCN网络,随后进入今天的重点——编码器-解码器架构。本教程禁止转载。同时,本教程来自知识星球【CV技术指南】更多技术教程,可加入星球学习。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV......
  • django token 认证 简单记录
    classUser(AbstractUser):username=models.CharField(max_length=20,unique=True,primary_key=True,verbose_name="用户名")email=models.EmailField(max_length=256,null=False,verbose_name="邮箱",blank=True)pass......