最近我开始使用基于 JWT 的身份验证。用户登录后,会生成一个用户令牌,如下所示
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ".
它由三个部分组成,每个部分用一个点(。)分隔。第一部分是 Base64 编码的标头。解码后我们会得到类似的东西
{
"alg": "HS256", //Algorithm used
"typ": "JWT"
}
第二部分是声明和 Base64 编码。解码后我们会得到类似的东西
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
第三部分是签名,由
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
*secret base64 encoded*
)
现在这个密钥是什么以及如何生成这个密钥?
我尝试了一些在线生成器,例如“http://kjur.github.io/jsjws/tool_jwt.html”,但得到了很多帮助。
由三部分组成的 Json Web Token。标头、有效负载和签名 现在,标头只是关于令牌本身的一些元数据,有效负载是我们可以编码到令牌中的数据,是我们真正想要的任何数据。所以我们想要在这里编码的数据越多,JWT 就越大。无论如何,这两个部分只是将被编码但未加密的纯文本。
所以任何人都可以解码并阅读它们,我们不能在这里存储任何敏感数据。但这根本不是问题,因为在第三部分,所以在签名中,才是真正有趣的地方。签名是使用标头、有效负载和保存在服务器上的秘密创建的。
https://i.stack.imgur.com/bOHqZ.png
一旦服务器接收到 JWT 以授予对受保护路由的访问权限,它需要对其进行验证以确定用户是否真的是他声称的那个人。换句话说,它将验证是否没有人更改令牌的标头和有效负载数据。同样,此验证步骤将检查是否没有第三方实际更改 Json Web 令牌的标头或有效负载。
那么,这种验证实际上是如何工作的呢?嗯,它实际上很简单。收到 JWT 后,验证将获取其标头和有效负载,并与仍保存在服务器上的秘密一起,基本上创建一个测试签名。
https://i.stack.imgur.com/b2dzI.png
因为如果它们已被修改,那么测试签名将必须不同。因此,在这种数据没有更改的情况下,我们可以对用户进行身份验证。当然,如果这两个签名实际上不同,那么这意味着有人篡改了数据。通常通过尝试更改有效负载。但是操纵有效载荷的第三方当然无法访问机密,因此他们无法签署 JWT。所以原始签名永远不会对应于被操纵的数据。因此,在这种情况下,验证总是会失败。这是使整个系统正常工作的关键。正是这种魔力让 JWT 变得如此简单,但也非常强大。
现在让我们用nodejs做一些练习:
配置文件非常适合存储 JWT SECRET 数据。对签名使用标准 HSA 256 加密,密钥长度至少应为 32 个字符,但越长越好。
配置.env:
JWT_SECRET = my-32-character-ultra-secure-and-ultra-long-secret
//after 90days JWT will no longer be valid, even the signuter is correct and everything is matched.
JWT_EXPIRES_IN=90
现在使用命令安装 JWT
npm i jsonwebtoken
用户注册后的示例,将 JWT 令牌传递给他,这样他就可以保持登录状态并访问资源。
exports.signup = catchAsync(async (req, res, next) => {
const newUser = await User.create({
name: req.body.name,
email: req.body.email,
password: req.body.password,
passwordConfirm: req.body.passwordConfirm,
});
const token = jwt.sign({ id: newUser._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN,
});
res.status(201).json({
status: 'success',
token,
data: {
newUser,
},
});
});
https://i.stack.imgur.com/yC6hV.png
在我看来,不要求助于第三方来生成你的超级密钥,因为你不能再说它是秘密的了。只需使用您的键盘。
用于签署 JWT 的算法 (HS256
) 意味着密钥是发送方和接收方都知道的对称密钥。它是在带外协商和分发的。因此,如果您是令牌的预期接收者,则发送者应该已经向您提供了带外秘密。
如果您是发送者,您可以使用任意字节字符串作为秘密,它可以生成或有意选择。您必须确保将秘密提供给带外的预期接收者。
作为记录,JWT 中的 3 个元素不是 base64 编码的,而是 base64url 编码的,它是 base64 编码的一种变体,可生成 URL 安全值。
什么是秘钥
密钥与标头和有效负载相结合以创建唯一的散列。如果您拥有密钥,您只能验证此哈希。
如何生成密钥
您可以选择一个好的长密码。或者,您可以从像 this 这样的网站生成它。
示例(但现在不要使用这个):
8Zz5tw0Ionm3XPZZfN0NOml3z9FMfmpgXwovR9fp6ryDIoGRM8EPHAB6iHsc0fb
/dev/urandom
在本地进行:unix.stackexchange.com/questions/230673/…
您可以编写自己的生成器。密钥本质上是一个字节数组。确保您转换为字节数组的字符串是 base64 编码的。
在 Java 中,你可以做这样的事情。
String key = "random_secret_key";
String base64Key = DatatypeConverter.printBase64Binary(key.getBytes());
byte[] secretBytes = DatatypeConverter.parseBase64Binary(base64Key);
密钥是做什么的,你可能已经知道了。它基本上是 HMAC SH256(安全哈希)。 Secret 是对称密钥。
使用相同的密钥,您可以生成、重新验证、编辑等。
为了更安全,您可以使用私钥、公钥(非对称方式)。用于创建令牌的私钥,用于在客户端级别验证的公钥。
来到秘密密钥给什么你可以给任何东西,“sudsif”,“sdfn2173”,任何长度
您可以使用在线生成器,或手动编写
我更喜欢使用 openssl
C:\Users\xyz\Desktop>openssl rand -base64 12
65JymYzDDqqLW8Eg
生成,然后使用 base 64 编码
C:\Users\xyz\Desktop>openssl rand -out openssl-secret.txt -hex 20
生成的值保存在名为“openssl-secret.txt”的文件中
生成并存储到文件中。
一件事是给 12 将生成,仅 12 个字符,但由于它是 base 64 编码,它将是 (4/3*n) 上限值。
我推荐阅读这篇文章
openssl rand <...args> | clip
将其复制到剪贴板而不是将其写入文件
如果您正在寻找 JWT_AUTH_SECRET_KEY 的密钥,那么您可以使用此处生成的任何密钥:
https://api.wordpress.org/secret-key/1.1/salt/
这通常用于“WP REST API 的 JWT 身份验证”( https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/ )
也许你是像我一样来这里寻找那个的人:D
要生成 64 字节的唯一密钥,请在节点中运行以下命令:
crypto.randomBytes(64).toString("hex");