如何签署 JWT?
Posted
技术标签:
【中文标题】如何签署 JWT?【英文标题】:How to sign JWT? 【发布时间】:2015-04-03 11:49:03 【问题描述】:我正在尝试保护 Sinatra API。
我正在使用ruby-jwt
创建 JWT,但我不知道具体用什么签名。
我正在尝试使用用户的 BCrypt password_digest
,但每次调用 password_digest
时它都会更改,导致我去验证时签名无效。
【问题讨论】:
@joelparkerhenderson 我评论了你的答案,但我从来没有得到答案。虽然是的,但它帮助了我。我会标记它,但请回答。 @joelparkerhenderson 我对答案投了赞成票,但没有接受。 @joelparkerhenderson 我现在才看到,谢谢。这是一个很好的答案。我在网上找不到任何关于这方面的好方法的信息,而且它真的很简单。 【参考方案1】:对于 RS256 公钥和私钥策略,您可以使用 Ruby OpenSSL lib:
生成密钥:
key = OpenSSL::PKey::RSA.new 2048
open 'private_key.pem', 'w' do |io| io.write key.to_pem end
open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end
从 .pem 文件加载密钥以签署令牌:
priv_key = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
token = JWT.encode payload, priv_key, 'RS256'
从 .pem 文件加载密钥以验证令牌(为此创建一个中间件):
begin
# env.fetch gets http header
bearer = env.fetch('HTTP_AUTHORIZATION').slice(7..-1)
pub_key = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
payload = JWT.decode bearer, pub_key, true, algorithm: 'RS256'
# access your payload here
@app.call env
rescue JWT::ExpiredSignature
[403, 'Content-Type' => 'text/plain' , ['The token has expired.']]
rescue JWT::DecodeError
[401, 'Content-Type' => 'text/plain' , ['A token must be passed.']]
rescue JWT::InvalidIssuerError
[403, 'Content-Type' => 'text/plain' , ['The token does not have a valid issuer.']]
rescue JWT::InvalidIatError
[403, 'Content-Type' => 'text/plain' , ['The token does not have a valid "issued at" time.']]
end
要在 .env
中使用 RSA 密钥而不是加载文件,您需要使用 gem 'dotenv'
并使用换行符 '\n'
将密钥作为单行变量导入。检查此question 以了解如何操作。示例:
PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nmineminemineminemine\nmineminemineminemine\nmineminemine...\n-----END PUBLIC KEY-----\n"
作为.env
PUBLIC_KEY
变量,加载key会变成这样:
key = OpenSSL::PKey::RSA.new ENV['PUBLIC_KEY']
【讨论】:
【参考方案2】:根据wikipedia,密码学中使用的密钥基本上就是这样,一把打开锁的钥匙。密钥应一致且可靠,但不易复制,就像您在家中使用的密钥一样。
正如this answer 中所述,密钥应该是随机生成的。但是,您仍然希望保留密钥以在整个应用程序中使用。通过使用来自 bcrypt 的密码摘要,您实际上是在使用从基本密钥(密码)派生的散列密钥。因为散列是随机的,所以正如你所说,这不是一个可靠的密钥。
使用SecureRandom.hex(64)
的上一个答案是创建初始基本应用程序密钥的好方法。但是,在生产系统中,您应该将其作为配置变量并将其存储,以便在应用程序的多次运行(例如,在服务器重新启动后,您不应该使所有用户的 JWT 无效)或在多个分布式服务器。 This article 给出了一个从 Rails 环境变量中提取密钥的示例。
【讨论】:
【参考方案3】:使用任何类型的应用程序密钥,而不是用户的 bcrypt 密码摘要。
例如,使用 dot env gem 和一个 .env 文件,其条目如下:
JWT_KEY=YOURSIGNINGKEYGOESHERE
我个人使用简单的随机十六进制字符串生成密钥:
SecureRandom.hex(64)
十六进制字符串仅包含 0-9 和 a-f,因此该字符串是 URL 安全的。
【讨论】:
好的,但是我应该用什么来生成实际的密钥?我在想Base64.encode64(SecureRandom.random_bytes(128))
之类的东西。这样可以吗?它应该更长还是不同?以上是关于如何签署 JWT?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 CryptoJS 中使用私钥 (pem) 签署 JWT?