本文详细介绍了JWT用户校验的基本概念、实现流程以及代码示例,帮助读者理解如何通过JWT进行用户身份认证和权限控制。文章涵盖了JWT生成、验证以及常见应用场景,确保读者能够掌握JWT用户校验的完整流程。JWT用户校验通过紧凑的令牌格式和加密签名,保证了数据的安全性和完整性。JWT广泛应用于分布式系统中的跨域资源共享和用户身份验证。
JWT(JSON Web Token)是一种基于 JSON 的开放标准(RFC 7519),用于在网络间安全地传输信息。JWT 由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。这些部分共同形成了一个紧凑的、URL 安全的令牌,非常适合通过 HTTP 请求中的 Authorization
头或 Cookie
传递。
JWT 的标准结构如下:
eyJhbGciOiAiSnJlYVtdIl0iLCAidHlwIjogIkpXVCJ9.CmVzdG1hbi5leGFtcGxlLmNvbQ.eyJzdWIiOiJib25kZW50IiwidXNlcm5hbWUiOiJib2R5IiwiaWF0IjoxNjM3ODI5ODIyLCJleHAiOjE2Mzc4MzE0MjIiLCJqdGkiOiIwYzhhZjVjMy1hZTQxLTQ5MjAtODViNy1kZTg2YmQ3ZjIwZTgifQ.d8Aqz87gYJGFGt9wHwU56Ojlv8xRJkFVn3G4qkFV
该字符串由三个部分组成,每部分由一个点(.
)分隔:
HS256
。一个 JSON 对象,描述令牌的类型(typ
)和所使用的签名算法(alg
),如下所示:
{ "alg": "HS256", "typ": "JWT" }
携带声明(Claims)的 JSON 对象,声明分为三类:公开声明、私有声明、注册声明。
iss
(签发者)、exp
(过期时间)、sub
(主题)、iat
(签发时间)等。uid
(用户唯一标识)、role
(角色)等。iss
(签发者)、exp
(过期时间)、sub
(主题)、aud
(预期用户)等。用于验证 JWT 的真实性。签名部分通过使用签发者的私钥(对于对称签名)或者公钥(对于非对称签名)对前两部分进行加密得到。
JWT 常见的应用场景包括:
用户校验的主要目的是确保用户身份的真实性,防止未授权的访问。在网站或应用中,用户校验通常包括用户登录、身份验证和权限控制等环节。通过用户校验,可以确保只有合法用户能够访问系统中的资源和服务。
JWT 通过其紧凑的、URL 安全的令牌来实现用户校验。用户登录时,服务器生成一个包含用户信息的 JWT 令牌,并将其发送给客户端。客户端在后续的 API 请求中携带该令牌以证明其身份。服务器通过验证令牌的有效性和完整性来确认用户身份。
Authorization
头的一部分发送给服务器。生成 JWT 令牌的工具和库有很多,例如 PyJWT
(Python)、jsonwebtoken
(Node.js)、jwt
(Java)等。这些库提供了一套完整的 API,用于生成、验证和解析 JWT 令牌。
在 Python 中,PyJWT
是一个常用的库,用于生成和验证 JWT 令牌。安装方式如下:
pip install pyjwt
pip install pyjwt
import jwt import datetime # 定义密钥 secret_key = 'thisissecretkey' # 生成 JWT 令牌 def create_jwt_token(user_id, username, role): # 创建负载数据 payload = { "user_id": user_id, "username": username, "role": role, "exp": datetime.datetime.utcnow() + datetime.timedelta(days=1) } # 生成 JWT 令牌 token = jwt.encode(payload, secret_key, algorithm='HS256') # 返回生成的令牌 return token # 使用示例 user_id = "001" username = "bob" role = "admin" generated_token = create_jwt_token(user_id, username, role) print("JWT 令牌:", generated_token)
该示例代码通过 jwt.encode
函数生成一个令牌。payload
包含了用户信息和过期时间,secret_key
是用于生成签名的密钥,algorithm
指定了使用的算法。
在上面的示例代码中,payload
中包含了一个 exp
字段,它表示令牌的过期时间。exp
字段是一个 Unix 时间戳,表示自 1970 年 1 月 1 日以来的秒数。在示例中,过期时间设置为从当前时间开始的 1 天后。
验证 JWT 的步骤如下:
npm install jsonwebtoken
const jwt = require('jsonwebtoken'); // 定义密钥 const secret_key = 'thisissecretkey'; // 校验 JWT 令牌 function verify_jwt_token(token) { try { const decoded = jwt.verify(token, secret_key); console.log("令牌已验证,有效数据:", decoded); return decoded; } catch (error) { console.error("令牌验证失败,错误信息:", error.message); return null; } } // 使用示例 const token = "eyJhbGciOiJodHRwOi8vdXMyLmNvbS8xLzEuMC8iLCJ0eXBlOiJKV1QifQ.eyJzdWIiOiJib2R5IiwidXNlcm5hbWUiOiJib2R5IiwiaWF0IjoxNjM3ODI5ODIyLCJleHAiOjE2Mzc4MzE0MjIiLCJqdGkiOiIwYzhhZjVjMy1hZTQxLTQ5MjAtODViNy1kZTg2YmQ3ZjIwZTgifQ.d8Aqz87gYJGFGt9wHwU56Ojlv8xRJkFVn3G4qkFV"; const decoded = verify_jwt_token(token); console.log("解码后的令牌内容:", decoded);
上面代码使用 jwt.verify
函数验证令牌的有效性和完整性。如果验证通过,则返回解码后的负载数据。否则,抛出异常并返回 null
。
如果 jwt.verify
抛出 TokenExpiredError
异常,则表示令牌已过期。
catch (error) { if (error instanceof jwt.TokenExpiredError) { console.error("令牌已过期"); } else { console.error("令牌验证失败,错误信息:", error.message); } return null; }
如果 jwt.verify
抛出 JsonWebTokenError
异常,则表示令牌被篡改或无效。
catch (error) { if (error instanceof jwt.JsonWebTokenError) { console.error("令牌无效或被篡改"); } else { console.error("令牌验证失败,错误信息:", error.message); } return null; }
搭建一个简单的 Node.js 和 Express 项目,实现用户注册和登录功能,并使用 JWT 进行用户校验。
npm install express jsonwebtoken body-parser
const express = require('express'); const jwt = require('jsonwebtoken'); const bodyParser = require('body-parser'); const app = express(); // 定义密钥 const secret_key = 'thisissecretkey'; // 使用 body-parser 中间件解析请求体 app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); // 用户信息存储(模拟数据库) const users = []; // 用户注册 app.post('/register', (req, res) => { const { username, password } = req.body; users.push({ username, password }); res.json({ message: '注册成功' }); }); // 用户登录 app.post('/login', (req, res) => { const { username, password } = req.body; const user = users.find(u => u.username === username && u.password === password); if (user) { const token = jwt.sign({ username }, secret_key, { expiresIn: '1h' }); res.json({ token }); } else { res.status(401).json({ message: '用户名或密码错误' }); } }); // 需要校验的 API 端点 app.get('/protected', (req, res) => { const token = req.headers.authorization.split(' ')[1]; if (!token) { return res.status(401).json({ message: '未授权' }); } jwt.verify(token, secret_key, (err, decoded) => { if (err) { return res.status(401).json({ message: '令牌验证失败' }); } res.json({ message: '访问成功', user: decoded.username }); }); }); // 启动服务器 app.listen(3000, () => { console.log('服务器运行在 http://localhost:3000'); });
该示例代码包含以下功能:
Authorization
头获取并验证 JWT 令牌,验证成功后允许访问资源。app.post('/register', (req, res) => { const { username, password } = req.body; users.push({ username, password }); res.json({ message: '注册成功' }); });
app.post('/login', (req, res) => { const { username, password } = req.body; const user = users.find(u => u.username === username && u.password === password); if (user) { const token = jwt.sign({ username }, secret_key, { expiresIn: '1h' }); res.json({ token }); } else { res.status(401).json({ message: '用户名或密码错误' }); } });
app.use((req, res, next) => { if (req.method === 'OPTIONS') { res.status(204).end(); } else { next(); } });
可以通过设置 Authorization
头来保持用户的会话状态。
app.get('/protected', (req, res) => { const token = req.headers.authorization.split(' ')[1]; if (!token) { return res.status(401).json({ message: '未授权' }); } jwt.verify(token, secret_key, (err, decoded) => { if (err) { return res.status(401).json({ message: '令牌验证失败' }); } res.json({ message: '访问成功', user: decoded.username }); }); });
HS256
)和强密钥来防止令牌伪造。Authorization
头。确保在请求中正确设置 Authorization
头。Strict-Transport-Security
头,强制客户端通过 HTTPS 访问资源。Authorization
头传递,也可以通过 Cookie
传递,但要确保 Cookie
设置为 HttpOnly
和 Secure
,防止通过 JavaScript 访问和修改。通过以上措施,可以确保 JWT 令牌的安全传输,提高系统的安全性。