在网站开发和现代验证领域中,安全性是最重要的考虑因素之一。开发者需采用有效的机制来保证在用户交互过程中数据的完整性和授权。JSON Web Tokens(JWT)作为一种认证和信息交流的安全方法,已广泛被采用。本文将详细介绍JWT的基本概念、结构、优点及其实施的最佳实践建议。
什么是JSON Web Token(JWT)?
JSON Web Token,通常简称为JWT,是一个开放标准(RFC 7519),用于以 JSON 对象的形式在各方之间传输信息,该 Token 经过数字签名,确保了信息的真实性和完整性。JWT主要用于用户认证,授权访问特定资源,并安全地交换信息。
JWT 的结构
JWT 由三个主要部分组成,这些部分以点(.)分隔,形成一个字符串。这三部分分别是 Header(头部)、Payload(负载)和 Signature(签名)。下面将详细介绍每一部分:
头通常由两部分组成:Token 的类型(typ),通常是 "JWT",和所使用的算法(alg),比如 HMAC SHA256(HS256)或者 RSA SHA256(RS256)。Header 用 JSON 编写,并使用 Base64Url 编码转换为字符串。例如:
{
"alg": "HS256",
"typ": "JWT"
}
Payload 部分包含称为声明(claims)的语句,这些声明包括注册声明、公共声明和私有声明。
-
注册声明:这些是预定义的声明,如 iss(发行人)、exp(到期时间)、sub(主题)等,它们不是必需的但被推荐使用以提供标准化的信息。
-
公共声明:可以添加任意的声明,但这里得注意,避免与其他注册声明发生冲突。
-
私有声明:由发送和接收两端共同定义和使用的声明,不应由外部实体认定。 Payload 同样使用 JSON 编写,并经过 Base64Url 编码处理。例如:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
为了生成 Token 的签名部分,必须要有编码后的 header 和 payload 才行(这里不是说 payload 不能为空,能空,不推荐,但是要有这东西才能算签名),以及一个 Key。然后使用 header 中指定的算法(如 HS256 或 RS256)对 header 和 payload 进行签名。这个签名用于验证消息在传递过程中没有被篡改;对于使用私钥签名的 Token,可以验证数据确实是由某个发送方发送的,发送方也不能抵赖。签名过程一般如下:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
最终,JWT 由这三部分的 Base64Url 编码连接而成,格式如下:
Base64UrlEncode(header).Base64UrlEncode(payload).Signature
JWT 的这种结构使其既可读又易于在网络上传输,同时还提供了必要的安全性,如消息完整性验证和认证。在实际应用中,JWT广泛用于实现无状态的认证机制,如 API 安全和单点登录(SSO)。
JWT 的优势
-
简洁和自包含: JWT 是一个自包含的 Token ,包括所有用户认证所需的信息,例如有关于用户的信息(claims),以及用于验证 Token的必要元数据。这种自包含性使得 JWT 非常适合分布式微服务架构中的跨服务认证。
-
安全性: JWT 支持通过数字签名的方式进行验证,使用公/私密钥对(如RS256)或一个密钥(如HS256)可以安全地验证 Token 的来源和完整性。只要保密信息(如签名密钥)不被泄露,JWT 就很难被篡改。
-
性能: JWT 的减少了需要进行数据库查询的次数,因为它可以直接从 Token 中解析出用户信息,而无需每次都查询数据库以验证用户的身份,这对于提高应用程序的响应速度和缩短加载时间很好用。
-
可扩展性: JWT 易于跨不同语言和平台传递。它基于 JSON,JSON 是大多数编程语言都支持的轻量级数据交换格式。这种兼容性使得 JWT 非常适合构建跨不同系统间和不同应用程序间的接口。
-
无状态和可伸缩性: 由于 JWT 不需要在服务器上持久存储会话信息,它自然支持无状态的认证机制。这一特点使得系统易于扩展,因为增加新的服务器不需要同步会话数据,服务器可以任意扩展和缩减,而不会影响用户的认证状态。
-
跨域认证: JWT 非常适合单页面应用(SPA)和跨域请求,因为它们可以通过 HTTP 头部简单地在不同的域之间传递,这也让 JWT 可以在多个系统中实现单点登录(SSO)。
-
有效期控制: JWT可以包含过期时间(Expiration Time)和生效时间(Not Before)声明,这为 Token 提供了自动的过期处理机制,增加了 Token 管理的安全性和便捷。
JWT 实践建议
-
优先使用强签名算法: 优先使用如 RS256(基于 RSA 的公钥/私钥对)之类的非对称签名算法,因为它的公钥可以安全地分发给任何需要验证JWT的一方,而私钥则保持安全地存储在发行者处。尽量避免使用较弱的对称算法如 HS256,除非在某些受控的内网环境中,因为对称加密算法还是需要共享密钥得。
-
不要在JWT中存储敏感信息: 尽管JWT是加密的,但还是别在 Token 中存储敏感信息,如用户密码或个人身份信息。JWT 可以通过 Base64 解码方法被解码,存储敏感信息会增加数据泄露的风险。
-
设置过期时间(如果可行的话): 为JWT设置过期时间(例如,我上家公司做的就是 30min),以减少 Token 被窃取并被滥用的风险。毕竟过期时间短就可以缩短攻击者窃取的 Token 使用窗口期。
-
Refresh Token: 对于需要长时间维持登录状态的应用,使用 Refresh Token 机制。这样,即使 Token 过期,用户也不必重新认证,而可以通过安全的方式获取新的 Access Token。
-
Token撤销机制: 虽然 JWT 是无状态的,但可以通过 Token 黑名单或把撤销的 Token 存储在数据库中来管理撤销过程。
-
安全传输: 这个老生常谈了,用 HTTPS 来传输 JWT,以避免中间人攻击;绝不要通过 URL 或 GET 参数传递 JWT,因为这可能会导致 Token 泄露到日志文件或 HTTP referer 头中。
-
错误处理要注意: 举个例子,认证失败时返回的错误信息别吐太多信息,我记得是哪家来?认证错误会吐用户的手机号,当时泄露了蛮多数据的。
-
CSRF 防护: 即使使用 JWT,也要通过使用同源策略和 CSRF Token 来增强安全性。
例子
我以 Node.js 为例吧,这里实现 JWT 认证涉及使用 jsonwebtoken 进行 Token 的创建和验证,以及使用 express 框架来搭建 Web 服务器。以下是一个 JWT 实现示例,包括 Access Token 和 Refresh Token 的处理:
初始化项目和安装依赖
mkdir jwt-auth-example
cd jwt-auth-example
npm init -y
npm install express jsonwebtoken dotenv
配置环境变量
ACCESS_TOKEN_SECRET=access_token
REFRESH_TOKEN_SECRET=refresh_token
// 创建一个名为server.js的文件和Express服务器,同时实现JWT的生成和验证功能:
require('dotenv').config();
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
const users = [
id: 1, username: 'example', password: 'password' } // 仅为示例,实际应用中应使用数据库和加密密码
];
生成Access Token
function generateAccessToken(user) {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
}
生成 Refresh Token
function generateRefreshToken(user) {
return jwt.sign(user, process.env.REFRESH_TOKEN_SECRET);
}
登录路由
(req, res) => {
const username = req.body.username;
const user = users.find(user => user.username === username);
if (!user || req.body.password !== user.password) {
return res.status(401).send('Username or password incorrect');
}
const accessToken = generateAccessToken({ username: user.username });
const refreshToken = generateRefreshToken({ username: user.username });
accessToken, refreshToken });
});
验证Access Token的中间件
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (token == null) return res.sendStatus(401);
process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
user; =
next();
});
}
保护的路由
authenticateToken, (req, res) => {
message: "You are accessing a protected route", user: req.user });
});
() => {
running on port 3000');
});
这个例子包括了登录、生成 Access Token 和访问受限资源,Refresh Token 在 Access Token 过期后获取新的 Access Token,但我的nodejs技术不太行,这里就不写出来丢人了。
JWT 与 OAuth 2.0
这俩货经常会被同时提起,但不要搞混了,OAuth 2.0 是授权框架,说人话就是你可以通过 OAuth 2.0 无需输入用户名密码就把你想授权的信息发给第三方,例如我想使用 Google 账户来登录 Linkedin,或者登 Medium 之类的,这种涉及第三方登录的场景你就可以用 OAuth 2.0;而 JWT 只是一个安全交换 Token 等信息的标准,你可以理解为 OAuth 2.0 可以用 JWT 来做认证,但也可以用 SAML Token,也可以用 MAC Token 等。
简而言之,JWT 是一种 Token 格式,可以用于在系统间安全地传输,而OAuth 2.0 是一个授权框架,用于控制第三方访问用户的资源。在OAuth 2.0中,JWT经常被用于实现授权流程。
总结
在本文中,我们探讨了 JWT 的基本结构、主要优点和实现时应遵循的最佳实践。还有 JWT 的诸多优势,如安全性、可扩展性、性能提升以及跨域认证能力等,这些使得 JWT 成为处理认证和信息交换的理想选择。
最后,我通过一个(不太好的) Node.js 示例,展示了如何在实际应用中安全有效地使用 JWT。这个示例涵盖了用户认证、Token 生成和验证,以及保护敏感资源的访问,提供了一套模板,可以根据具体需要进行调整和扩展。
点个在看你最好看
原文始发于微信公众号(imBobby的自留地):JSON Web Token(JWT):安全的 Web 身份验证方法
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论