服务器如何验证JWT?公钥从哪里获取?

5
我正在查看 Node.js 中 JWT 令牌的 示例,以及其中的 verify 函数。我的问题是,在 verify(token, publicKey) 中的 publicKey 是从哪里来的?流程是什么?

客户端(我的用户之一)已在其计算机/服务器上安装了客户端库,用于向我的应用程序myapp.com发出请求。在myapp.com服务器中,我调用verify(token, publicKey)。在客户端库中,我使用privateKey生成令牌。问题是,通常客户端如何获得此私钥?(即是否类似于heroku login从底层下载专用于进行JWT请求的私钥?)。而如何获取公钥?我的理解是,客户端会下载一个私钥,我们的服务器将存储公钥。然后,只需拥有公钥和令牌,就可以调用verify(token, publicKey)。但是,通常情况下,如何为服务器上的令牌获取公钥?服务器是否存储每个分发给客户端安装库的私钥的一个公钥?

1个回答

6
我通常看到的JWT使用方式是,只有少数受信任的发行者,通常只有一个,并且令牌被用作承载令牌。在许多情况下,发行者和验证器是相同的。在其他情况下,验证器信任一个身份提供者(例如Google),并从https URL(example)获取公钥。
在你的情况下,你可以充当发行者和验证器:
  1. (服务器)将生成一对密钥。
  2. 你的API服务器将信任由此密钥签名的JWT(它们只需要公钥,因为它们只需要验证它们)。
  3. 一个认证/管理服务器将拥有私钥,将验证用户并颁发JWT。
  4. 客户端永远不会处理任何密钥,他们只需存储已签名的JWT,并在进行客户端请求时传递它作为承载令牌。

这是例如GitHub使用的方法(此处)。在这种情况下,发行者和验证器都属于您。对于您和客户端来说,这种方法都是最简单的(一旦验证签名,您可以信任JWT的内容,而客户端只需处理不透明的API密钥,无需处理JWT的复杂性)。


一种可能的替代方法是:
  1. 生成密钥对,并将公钥与账户关联。这可以通过多种方式完成(见下文),但最终结果相同:客户端有一个私钥,您的服务器知道相应的公钥和它所关联的用户
  2. 在发出请求时,客户端创建一个JWT,用其私钥签名,并在令牌中包含其用户名(例如,在iss和/或sub字段中)。
  3. 您的服务器接收令牌,提取用户名,在数据库中查找与该用户帐户关联的公钥,并验证令牌。

例如,Google Cloud 用于服务帐户身份验证 就使用了这种方法。

第1步可以通过两种方式完成:

  • 您需要对用户进行身份验证,生成一对密钥,并将公钥与账户关联,然后让用户下载他们的私钥(当然是通过https)。虽然通常认为为他人生成密钥有点不好(因为您可以看到不需要知道的密钥并必须通过网络发送它),但这样做更容易,Google正在这样做。
  • 用户生成并存储密钥对。您对用户进行身份验证,用户上传公钥,您将其与账户关联。

无论哪种方式,如果您选择“用户签署JWT”方法,则可能需要提供客户端库或至少代码示例。还请注意Google的要求,即将长期有效的令牌视为无效,以强制执行令牌的短期有效性。如果没有这个规则和执行,那么许多客户端开发人员将对您的复杂解决方案感到困扰,手动签署在他的笔记本电脑上永久有效的令牌,然后将其用作持有者令牌。


heroku login实际上根本不使用JWT。它检索并存储OAuth Bearer Token。这与第一种方法最为相似(客户端从未处理任何私钥,只获取一个不透明的Blob,它恰好是可以验证的JWT)。非JWT令牌和长期JWT之间的区别在于,您的API服务器必须在数据库中查找常规令牌的含义和有效性,而JWT直接告诉您用户身份,以及可能包括的权限和其他属性,当您发行它时。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接