文章目录概念介绍 什么是 Access Token访问令牌 什么是 JWTJSON Web TokenJWT 的结构 具体代码示例Python 实战 总结与最佳实践签名密钥Secret Key如何生成️ 方法一使用命令行工具快速生成最推荐 方法二在编程语言中动态生成⚙️ 方法三使用在线生成工具 密钥生成与管理的核心原则JWT和Access Token的关系 核心关系抽象概念 vs 具体实现 它们到底是什么关系1. 包含与被包含的关系2. 为什么现在大家总把两者混为一谈⚖️ 直观对比普通 Access Token vs JWT 格式的 Access Token 总结Bearer 是什么意思️ Bearer 的核心含义认票不认人 标准格式 为什么要加 Bearer 这个词 安全提醒如何将Token存放在HttpOnly Cookie️ 第一步后端设置并下发 HttpOnly Cookie 第二步前端正常发送请求跨域需特殊处理 第三步后端验证 Token⚠️ 注意事项与避坑指南概念介绍JWTJSON Web Token和 Access Token访问令牌是现代网络应用中实现身份验证与授权的核心概念。为了让你全面理解我们将分别深入介绍它们并给出具体的代码示例。 什么是 Access Token访问令牌Access Token 就像是一张**“数字门票”或“临时通行证”**。当用户成功登录系统后服务器会颁发一个 Access Token 给客户端。在后续的请求中客户端只需携带这张“门票”服务器就能识别用户的身份并判断其是否有权访问受保护的资源如查看个人资料、获取订单数据等。核心作用用于授权Authorization即告诉服务器“我有权做这件事”。常见格式它可以是一个不透明的随机字符串但如今最主流的格式是JWT。生命周期为了安全起见Access Token 的有效期通常较短例如 15 分钟到 2 小时。过期后用户需要重新登录或通过刷新令牌Refresh Token来获取新的 Access Token。在实际的 HTTP 请求中Access Token 通常被放在请求头Header中格式如下Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... 什么是 JWTJSON Web TokenJWTJSON Web Token是一种开放标准RFC 7519它定义了一种紧凑且自包含的方式用于在各方之间安全地以 JSON 对象的形式传输信息。你可以把它理解为一种特定格式的 Access Token。JWT 最大的特点是无状态和自包含。这意味着服务器不需要在数据库或内存中存储会话信息因为 JWT 本身就包含了所有必要的用户信息和防篡改签名。JWT 的结构一个完整的 JWT 由三部分组成中间用点号.分隔看起来像这样xxxxx.yyyyy.zzzzz。这三部分分别是头部Header、载荷Payload和签名Signature。1. 头部Header头部通常包含两部分令牌的类型即 JWT以及所使用的签名算法如 HMAC SHA256 或 RSA。{alg:HS256,// 签名算法typ:JWT// 令牌类型}这部分内容经过 Base64Url 编码后形成了 JWT 的第一部分。2. 载荷Payload载荷用来存放实际需要传递的数据称为声明 Claims。它包含三种类型的声明注册声明预定义的标准字段如iss(签发者)、exp(过期时间)、sub(面向的用户/主题)、iat(签发时间) 等。公共声明使用者自定义的公开字段。私有声明通信双方约定的自定义字段例如user_id、role等。⚠️重要安全提醒载荷只是经过了 Base64Url编码而不是加密任何拿到 JWT 的人都可以轻松解码并看到里面的内容。因此绝对不要在 JWT 中存放密码、银行卡号等敏感信息。载荷示例{sub:1234567890,name:张三,role:admin,iat:1516239022,exp:1516242622}3. 签名Signature签名是 JWT 的核心用于验证消息在传输过程中未被篡改并确认签发者的身份。以 HS256 算法为例签名的生成方式是将编码后的 Header 和 Payload 用点号连接再配合服务端绝密的密钥Secret进行加密运算。HMACSHA256( base64UrlEncode(header) . base64UrlEncode(payload), secret )如果黑客试图修改 Payload 中的权限比如把role从user改为admin由于他没有服务端的secret生成的签名就会与服务端计算的不一致服务器便会拒绝该请求。 具体代码示例Python 实战在实际开发中我们通常会使用成熟的第三方库来生成和验证 JWT。以下使用 Python 的官方推荐库PyJWT来演示。首先安装依赖pip install pyjwtimportjwtimportdatetime# 实际项目中SECRET_KEY 应该存放在环境变量中绝不能硬编码SECRET_KEYyour-super-secret-key-keep-it-safe# 1. 生成 JWT (作为 Access Token)defcreate_access_token(user_id,role):# 准备载荷Payloadpayload{user_id:user_id,role:role,# 设置过期时间为当前时间往后推1小时exp:datetime.datetime.now(datetime.timezone.utc)datetime.timedelta(hours1),# 签发时间iat:datetime.datetime.now(datetime.timezone.utc)}# 生成 JWT指定算法为 HS256tokenjwt.encode(payload,SECRET_KEY,algorithmHS256)returntoken# 2. 验证和解码 JWTdefverify_access_token(token):try:# 解码并验证库会自动检查 exp 是否过期以及签名是否正确decoded_payloadjwt.decode(token,SECRET_KEY,algorithms[HS256])print(✅ 令牌验证通过解析出的用户信息:,decoded_payload)returndecoded_payloadexceptjwt.ExpiredSignatureError:print(❌ 令牌已过期请重新登录)exceptjwt.InvalidTokenErrorase:print(f❌ 无效的令牌可能被篡改:{e})# --- 测试运行 ---if__name____main__:# 模拟用户登录生成令牌access_tokencreate_access_token(user_id1001,roleadmin)print(生成的 Access Token (JWT):,access_token)print(-*50)# 模拟客户端携带令牌访问接口服务端进行验证verify_access_token(access_token) 总结与最佳实践JWT vs Access TokenAccess Token 是一个抽象的概念通行证而 JWT 是实现这个通行证的一种非常流行的技术标准自带信息的通行证。无状态优势JWT 非常适合分布式系统和微服务架构因为各个服务只需要共享同一个密钥即可验证令牌无需频繁查询数据库或共享 Session。安全建议始终使用 HTTPS 传输防止令牌在网络中被截获。设置合理的短期过期时间如 1 小时。前端存储时推荐使用安全的httpOnly Cookie而非localStorage以防范 XSS 攻击。确保签名密钥Secret Key足够复杂且妥善保管。签名密钥Secret Key如何生成生成 JWT 的签名密钥Secret Key绝不能像普通密码那样随意设定比如 “123456” 或 “mysecret”。为了保证系统安全必须使用**密码学安全的伪随机数生成器CSPRNG**来生成足够长且完全随机的字符串。特别是当你使用主流的HS256 (HMAC-SHA256)算法时JWT 规范强制要求密钥长度至少为 256 位即 32 字节。如果密钥过短在代码运行时通常会直接抛出WeakKeyException异常。以下为你提供几种在不同场景下生成高安全性 Secret Key 的方法️ 方法一使用命令行工具快速生成最推荐如果你熟悉终端操作这是最快且极其安全的方式。macOS / Linux 用户使用 OpenSSL直接在终端执行以下命令它会生成一个 32 字节的 Base64 编码随机字符串非常适合作为 JWT 密钥openssl rand-base6432示例输出8v/4kL2m9P0qR1sT3uV5wX7yZ9aB1cD3eF5gH7iJ9kLNode.js 环境用户如果你在开发 Node.js 项目可以直接在终端运行这行代码来生成 32 字节的十六进制密钥node-econsole.log(require(crypto).randomBytes(32).toString(hex))示例输出a47f7c20e4f9a87c6d0af6e6c8c4bc25d2a8e7c4a9f5c30f72db7d9a48f1c3d2 方法二在编程语言中动态生成如果你需要在程序启动时自动生成并保存密钥可以使用各语言自带的加密库。以下是 Java 和 Python 的安全生成示例Java 示例使用 JJWT 官方推荐的Keys工具类importio.jsonwebtoken.security.Keys;importjavax.crypto.SecretKey;importjava.util.Base64;// 方式1让库自动为你生成符合 HS256 要求的 256 位安全密钥SecretKeykeyKeys.secretKeyFor(io.jsonwebtoken.SignatureAlgorithm.HS256);// 方式2将生成的二进制密钥转为 Base64 字符串方便存入配置文件Stringbase64KeyBase64.getEncoder().encodeToString(key.getEncoded());System.out.println(生成的安全密钥: base64Key);Python 示例使用 secrets 模块importsecrets# 生成 32 字节256位的随机十六进制字符串secure_keysecrets.token_hex(32)print(f生成的安全密钥:{secure_key})⚙️ 方法三使用在线生成工具如果你不想敲命令也不想写代码可以使用专业的在线密码生成器。只需确保设置如下参数长度至少 32 个字符建议 44 字符以上如果是 Base64 格式。字符集勾选所有选项大写字母、小写字母、数字、特殊符号。注意出于最高级别的安全考量生产环境的绝密密钥建议优先在本地离线生成。 密钥生成与管理的核心原则无论你使用哪种方法请务必遵守以下安全红线严禁硬编码绝对不要把生成的密钥直接写死在源代码里例如SECRET_KEY xxx。一旦代码上传到 GitHub 等公开仓库你的整个认证系统将瞬间沦陷。使用环境变量将生成好的密钥存放在.env文件或服务器的环境变量中如JWT_SECRET你生成的超长随机字符串在代码中通过读取环境变量的方式来调用。Base64 只是编码不是加密生成的 Base64 字符串如SGVsbG9Xb3JsZA任何人都能解码看到原文。它只是为了方便传输和存储本身并不具备保密性因此这个字符串必须像保护数据库密码一样严加看管。JWT和Access Token的关系理解 Access Token 和 JWT 的关系其实只需要记住一句话Access Token 是“职位/角色”而 JWT 是实现这个角色的其中一种“具体人选”。为了让你彻底搞懂它们的区别与联系我们可以通过一个生活化的类比、一张对比表以及技术演进的角度来详细拆解。 核心关系抽象概念 vs 具体实现Access Token访问令牌是一个抽象的概念。它指的是在 OAuth 2.0 等授权协议中客户端用来换取服务器资源的那串“凭证”。协议只规定了它的作用证明你有权限但没有规定它必须长什么样。JWTJSON Web Token是一种具体的技术标准/格式。它定义了令牌内部该如何编码、如何签名、包含哪些字段。 生活化类比Access Token就像是“门票”这个词。它的作用是让你进入游乐园。JWT就像是“印有二维码和防伪水印的纸质票”。这是一种非常流行的门票制作形式。但是“门票”也可以是其他形式比如老式的“打孔硬卡纸”或者“手环”。 它们到底是什么关系1. 包含与被包含的关系并不是所有的 Access Token 都是 JWT但 JWT 经常被用作 Access Token。Access Token 的表现形式通常有两种不透明字符串 (Opaque String)比如4a8f7b2c1d9e。这种令牌本身没有任何意义就像一把随机的钥匙。服务器收到后必须去数据库里查“这把钥匙对应哪个用户过期了吗”传统 Session 模式常用。自包含字符串 (Self-contained)也就是JWT。比如eyJhbG...xxxx.yyyy。服务器收到后不需要查数据库直接通过数学公式验签就能算出它是谁、有没有被篡改。2. 为什么现在大家总把两者混为一谈因为在现代前后端分离、微服务架构的开发中使用 JWT 作为 Access Token 是最主流、最高效的方案。所以很多开发者在日常交流时习惯性地把 Access Token 等同于 JWT但在严谨的技术定义上它们不能划等号。⚖️ 直观对比普通 Access Token vs JWT 格式的 Access Token假设两个用户都要访问“获取订单信息”的接口他们携带的 Access Token 处理方式完全不同特性传统不透明 Access TokenJWT 格式的 Access Token外观示例gAAAABBBBCCCC12345(随机乱码)eyJhbGciOiJ...xxxxx.yyyyy(三段式结构)服务器验证方式查库拿着 Token 去数据库/Redis 查询对应的用户信息。计算本地通过算法解密并验证签名无需查库。性能每次请求都要访问数据库高并发时数据库压力大。纯CPU计算速度极快非常适合分布式系统。包含信息无信息全在服务端数据库里。有Payload 里自带 UserID、角色等信息。注销难度容易直接在数据库删掉这条记录即可。较难因为无状态通常需要配合黑名单或短有效期。 总结你可以这样向别人介绍它们的关系Access Token是为了保护 API 而设立的一道关卡任何能通关的凭证都叫 Access Token。JWT是目前最流行的一种通关凭证样式。它把用户信息直接写在了凭证上并且加了防伪印章让守门员服务器一眼就能识别不用再去翻花名册查数据库。所以在实际工作中当你的领导或同事说“生成一个 Access Token”时大概率他们指的就是“生成一个 JWT 字符串”。Bearer 是什么意思Bearer翻译成中文就是**“持有者”或“携带者”**的意思。在 HTTP 身份验证尤其是使用 JWT 作为 Access Token中它代表了一种非常直接的授权逻辑“谁手里拿着这个令牌Token服务器就认为谁是合法用户。”️ Bearer 的核心含义认票不认人当你在请求头中看到Authorization: Bearer 你的JWT时它的意思是“你好服务器我是这个令牌的持有者Bearer。我把这张门票交给你看请允许我访问受保护的资源。”服务器收到后不会去核实“你是不是最初登录的那个人”比如不会让你再输一次密码或做人脸识别而是只检查你手里的这张票据JWT是否真实、是否在有效期内。只要票据是真的服务器就放行。 标准格式在 HTTP 请求的 Header 中Bearer 认证的标准写法是固定的Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...Authorization这是标准的 HTTP 请求头字段名表示“我要进行身份验证”。Bearer这是认证方案Scheme告诉服务器“我后面跟着的是一个令牌请你按‘持有者’的规则来验证它”。eyJhb…这就是你的 JWTAccess Token。注意Bearer和Token之间必须有一个空格。 为什么要加 “Bearer” 这个词你可能会问直接发 Token 不行吗为什么要多写一个单词这是因为 HTTP 协议支持多种身份验证方式。加上Bearer是为了明确告诉服务器当前使用的是哪种规则。常见的还有BasicAuthorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ通常用于传用户名和密码的 Base64 编码Digest一种比 Basic 更安全的摘要认证方式。所以Bearer就像是一个“暗号前缀”服务器一看到它就知道“哦接下来要处理的是一个 JWT/Access Token我要去验签了。” 安全提醒正因为 Bearer 认证是**“认票不认人”**所以保护你的 Access TokenJWT至关重要如果黑客通过抓包、XSS 攻击等手段窃取了你的 JWT他就可以伪装成你成为合法的 Bearer随意调用你的接口。这也是为什么现代 Web 开发强烈建议使用HTTPS防止半路被抢走票据以及将 Token 存放在HttpOnly Cookie防止被网页里的恶意脚本偷走的原因。如何将Token存放在HttpOnly Cookie将 Token 存放在 HttpOnly Cookie 中是保护用户身份凭证如 JWT免受 XSS跨站脚本攻击的最佳实践。实现这一点的核心原则是Token 绝对不能由前端 JavaScript 来设置或读取必须完全交由后端在服务端通过 HTTP 响应头下发并由浏览器自动管理。下面为你详细拆解从后端到前端的完整落地流程️ 第一步后端设置并下发 HttpOnly Cookie当用户登录成功时后端在返回的 HTTP 响应中通过Set-Cookie响应头将 Token 写入浏览器的 Cookie 中并附加严格的安全属性。核心安全属性说明HttpOnly这是最关键的属性。它禁止前端 JavaScript如document.cookie访问该 Cookie从根本上杜绝了 XSS 窃取 Token 的风险。Secure强制要求该 Cookie 只能在 HTTPS 协议下传输防止在网络传输过程中被中间人窃听。SameSite限制第三方网站发起的请求携带此 Cookie。建议设置为Strict或Lax能有效防御 CSRF跨站请求伪造攻击。Path通常设置为/表示该 Cookie 在整个域名下的所有路径都有效。代码示例以 Node.js/Express 为例app.post(/api/login,(req,res){// 1. 验证用户账号密码...// 2. 生成 JWT Token...consttokengenerateJWT(user);// 3. 将 Token 放入 HttpOnly Cookie 中下发给客户端res.cookie(access_token,token,{httpOnly:true,// 核心JS无法读取防XSSsecure:process.env.NODE_ENVproduction,// 生产环境仅HTTPS传输sameSite:strict,// 严格限制跨站请求防CSRFmaxAge:24*60*60*1000,// 有效期1天path:/// 全站生效});res.status(200).json({message:登录成功});}); 第二步前端正常发送请求跨域需特殊处理一旦后端设置了 HttpOnly Cookie浏览器就会接管后续工作。同域场景当前端和后端部署在同一个域名下时浏览器会在每次向该域名发起 HTTP 请求时自动在请求头中带上这个 Cookie。前端代码无需做任何额外操作直接调用接口即可。跨域场景如果前后端分离且存在跨域例如前端在www.example.com后端 API 在api.example.com你需要在前端的请求库中显式开启“携带凭证”的配置。前端跨域请求配置示例使用 Axiosimportaxiosfromaxios;// 全局配置允许跨域请求自动携带 Cookie (即 HttpOnly Token)axios.defaults.withCredentialstrue;axios.defaults.baseURLhttps://api.example.com;constgetUserInfoasync(){// 浏览器会自动把 access_token 放在 Cookie 请求头中发往后端constresawaitaxios.get(/user/info);returnres.data;}; 第三步后端验证 Token当受保护的接口收到请求时后端直接从请求头的Cookie字段中提取 Token 并进行验签而不是去提取Authorization头。后端验证逻辑示例functionauthMiddleware(req,res,next){// 从请求的 Cookie 中获取 Tokenconsttokenreq.cookies.access_token;if(!token){returnres.status(401).json({message:未提供身份凭证});}try{// 验证 Token 的有效性是否过期、签名是否正确constdecodedverifyJWT(token);req.userdecoded;// 将解析出的用户信息挂载到请求对象上next();}catch(error){returnres.status(401).json({message:身份凭证无效或已过期});}}⚠️ 注意事项与避坑指南绝对不要在 localStorage 中存储敏感 Token很多开发者习惯用localStorage.setItem(token, xxx)但这会让 Token 暴露在document.cookie或全局 JS 环境中一旦发生 XSS 攻击黑客可以轻易盗取你的 Token。CSRF 防护不能少虽然SameSite属性已经能抵御大部分 CSRF 攻击但在涉及修改数据的关键操作如转账、改密时建议后端依然配合传统的 CSRF Token 机制进行双重校验。退出登录的实现由于前端无法删除 HttpOnly Cookie退出登录必须由前端调用后端的登出接口后端再通过Set-Cookie将该 Cookie 清空通常是将maxAge设为 0 或赋一个空值。
JWT(JSON Web Token)和 Access Token(访问令牌)
发布时间:2026/6/2 0:19:17
文章目录概念介绍 什么是 Access Token访问令牌 什么是 JWTJSON Web TokenJWT 的结构 具体代码示例Python 实战 总结与最佳实践签名密钥Secret Key如何生成️ 方法一使用命令行工具快速生成最推荐 方法二在编程语言中动态生成⚙️ 方法三使用在线生成工具 密钥生成与管理的核心原则JWT和Access Token的关系 核心关系抽象概念 vs 具体实现 它们到底是什么关系1. 包含与被包含的关系2. 为什么现在大家总把两者混为一谈⚖️ 直观对比普通 Access Token vs JWT 格式的 Access Token 总结Bearer 是什么意思️ Bearer 的核心含义认票不认人 标准格式 为什么要加 Bearer 这个词 安全提醒如何将Token存放在HttpOnly Cookie️ 第一步后端设置并下发 HttpOnly Cookie 第二步前端正常发送请求跨域需特殊处理 第三步后端验证 Token⚠️ 注意事项与避坑指南概念介绍JWTJSON Web Token和 Access Token访问令牌是现代网络应用中实现身份验证与授权的核心概念。为了让你全面理解我们将分别深入介绍它们并给出具体的代码示例。 什么是 Access Token访问令牌Access Token 就像是一张**“数字门票”或“临时通行证”**。当用户成功登录系统后服务器会颁发一个 Access Token 给客户端。在后续的请求中客户端只需携带这张“门票”服务器就能识别用户的身份并判断其是否有权访问受保护的资源如查看个人资料、获取订单数据等。核心作用用于授权Authorization即告诉服务器“我有权做这件事”。常见格式它可以是一个不透明的随机字符串但如今最主流的格式是JWT。生命周期为了安全起见Access Token 的有效期通常较短例如 15 分钟到 2 小时。过期后用户需要重新登录或通过刷新令牌Refresh Token来获取新的 Access Token。在实际的 HTTP 请求中Access Token 通常被放在请求头Header中格式如下Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... 什么是 JWTJSON Web TokenJWTJSON Web Token是一种开放标准RFC 7519它定义了一种紧凑且自包含的方式用于在各方之间安全地以 JSON 对象的形式传输信息。你可以把它理解为一种特定格式的 Access Token。JWT 最大的特点是无状态和自包含。这意味着服务器不需要在数据库或内存中存储会话信息因为 JWT 本身就包含了所有必要的用户信息和防篡改签名。JWT 的结构一个完整的 JWT 由三部分组成中间用点号.分隔看起来像这样xxxxx.yyyyy.zzzzz。这三部分分别是头部Header、载荷Payload和签名Signature。1. 头部Header头部通常包含两部分令牌的类型即 JWT以及所使用的签名算法如 HMAC SHA256 或 RSA。{alg:HS256,// 签名算法typ:JWT// 令牌类型}这部分内容经过 Base64Url 编码后形成了 JWT 的第一部分。2. 载荷Payload载荷用来存放实际需要传递的数据称为声明 Claims。它包含三种类型的声明注册声明预定义的标准字段如iss(签发者)、exp(过期时间)、sub(面向的用户/主题)、iat(签发时间) 等。公共声明使用者自定义的公开字段。私有声明通信双方约定的自定义字段例如user_id、role等。⚠️重要安全提醒载荷只是经过了 Base64Url编码而不是加密任何拿到 JWT 的人都可以轻松解码并看到里面的内容。因此绝对不要在 JWT 中存放密码、银行卡号等敏感信息。载荷示例{sub:1234567890,name:张三,role:admin,iat:1516239022,exp:1516242622}3. 签名Signature签名是 JWT 的核心用于验证消息在传输过程中未被篡改并确认签发者的身份。以 HS256 算法为例签名的生成方式是将编码后的 Header 和 Payload 用点号连接再配合服务端绝密的密钥Secret进行加密运算。HMACSHA256( base64UrlEncode(header) . base64UrlEncode(payload), secret )如果黑客试图修改 Payload 中的权限比如把role从user改为admin由于他没有服务端的secret生成的签名就会与服务端计算的不一致服务器便会拒绝该请求。 具体代码示例Python 实战在实际开发中我们通常会使用成熟的第三方库来生成和验证 JWT。以下使用 Python 的官方推荐库PyJWT来演示。首先安装依赖pip install pyjwtimportjwtimportdatetime# 实际项目中SECRET_KEY 应该存放在环境变量中绝不能硬编码SECRET_KEYyour-super-secret-key-keep-it-safe# 1. 生成 JWT (作为 Access Token)defcreate_access_token(user_id,role):# 准备载荷Payloadpayload{user_id:user_id,role:role,# 设置过期时间为当前时间往后推1小时exp:datetime.datetime.now(datetime.timezone.utc)datetime.timedelta(hours1),# 签发时间iat:datetime.datetime.now(datetime.timezone.utc)}# 生成 JWT指定算法为 HS256tokenjwt.encode(payload,SECRET_KEY,algorithmHS256)returntoken# 2. 验证和解码 JWTdefverify_access_token(token):try:# 解码并验证库会自动检查 exp 是否过期以及签名是否正确decoded_payloadjwt.decode(token,SECRET_KEY,algorithms[HS256])print(✅ 令牌验证通过解析出的用户信息:,decoded_payload)returndecoded_payloadexceptjwt.ExpiredSignatureError:print(❌ 令牌已过期请重新登录)exceptjwt.InvalidTokenErrorase:print(f❌ 无效的令牌可能被篡改:{e})# --- 测试运行 ---if__name____main__:# 模拟用户登录生成令牌access_tokencreate_access_token(user_id1001,roleadmin)print(生成的 Access Token (JWT):,access_token)print(-*50)# 模拟客户端携带令牌访问接口服务端进行验证verify_access_token(access_token) 总结与最佳实践JWT vs Access TokenAccess Token 是一个抽象的概念通行证而 JWT 是实现这个通行证的一种非常流行的技术标准自带信息的通行证。无状态优势JWT 非常适合分布式系统和微服务架构因为各个服务只需要共享同一个密钥即可验证令牌无需频繁查询数据库或共享 Session。安全建议始终使用 HTTPS 传输防止令牌在网络中被截获。设置合理的短期过期时间如 1 小时。前端存储时推荐使用安全的httpOnly Cookie而非localStorage以防范 XSS 攻击。确保签名密钥Secret Key足够复杂且妥善保管。签名密钥Secret Key如何生成生成 JWT 的签名密钥Secret Key绝不能像普通密码那样随意设定比如 “123456” 或 “mysecret”。为了保证系统安全必须使用**密码学安全的伪随机数生成器CSPRNG**来生成足够长且完全随机的字符串。特别是当你使用主流的HS256 (HMAC-SHA256)算法时JWT 规范强制要求密钥长度至少为 256 位即 32 字节。如果密钥过短在代码运行时通常会直接抛出WeakKeyException异常。以下为你提供几种在不同场景下生成高安全性 Secret Key 的方法️ 方法一使用命令行工具快速生成最推荐如果你熟悉终端操作这是最快且极其安全的方式。macOS / Linux 用户使用 OpenSSL直接在终端执行以下命令它会生成一个 32 字节的 Base64 编码随机字符串非常适合作为 JWT 密钥openssl rand-base6432示例输出8v/4kL2m9P0qR1sT3uV5wX7yZ9aB1cD3eF5gH7iJ9kLNode.js 环境用户如果你在开发 Node.js 项目可以直接在终端运行这行代码来生成 32 字节的十六进制密钥node-econsole.log(require(crypto).randomBytes(32).toString(hex))示例输出a47f7c20e4f9a87c6d0af6e6c8c4bc25d2a8e7c4a9f5c30f72db7d9a48f1c3d2 方法二在编程语言中动态生成如果你需要在程序启动时自动生成并保存密钥可以使用各语言自带的加密库。以下是 Java 和 Python 的安全生成示例Java 示例使用 JJWT 官方推荐的Keys工具类importio.jsonwebtoken.security.Keys;importjavax.crypto.SecretKey;importjava.util.Base64;// 方式1让库自动为你生成符合 HS256 要求的 256 位安全密钥SecretKeykeyKeys.secretKeyFor(io.jsonwebtoken.SignatureAlgorithm.HS256);// 方式2将生成的二进制密钥转为 Base64 字符串方便存入配置文件Stringbase64KeyBase64.getEncoder().encodeToString(key.getEncoded());System.out.println(生成的安全密钥: base64Key);Python 示例使用 secrets 模块importsecrets# 生成 32 字节256位的随机十六进制字符串secure_keysecrets.token_hex(32)print(f生成的安全密钥:{secure_key})⚙️ 方法三使用在线生成工具如果你不想敲命令也不想写代码可以使用专业的在线密码生成器。只需确保设置如下参数长度至少 32 个字符建议 44 字符以上如果是 Base64 格式。字符集勾选所有选项大写字母、小写字母、数字、特殊符号。注意出于最高级别的安全考量生产环境的绝密密钥建议优先在本地离线生成。 密钥生成与管理的核心原则无论你使用哪种方法请务必遵守以下安全红线严禁硬编码绝对不要把生成的密钥直接写死在源代码里例如SECRET_KEY xxx。一旦代码上传到 GitHub 等公开仓库你的整个认证系统将瞬间沦陷。使用环境变量将生成好的密钥存放在.env文件或服务器的环境变量中如JWT_SECRET你生成的超长随机字符串在代码中通过读取环境变量的方式来调用。Base64 只是编码不是加密生成的 Base64 字符串如SGVsbG9Xb3JsZA任何人都能解码看到原文。它只是为了方便传输和存储本身并不具备保密性因此这个字符串必须像保护数据库密码一样严加看管。JWT和Access Token的关系理解 Access Token 和 JWT 的关系其实只需要记住一句话Access Token 是“职位/角色”而 JWT 是实现这个角色的其中一种“具体人选”。为了让你彻底搞懂它们的区别与联系我们可以通过一个生活化的类比、一张对比表以及技术演进的角度来详细拆解。 核心关系抽象概念 vs 具体实现Access Token访问令牌是一个抽象的概念。它指的是在 OAuth 2.0 等授权协议中客户端用来换取服务器资源的那串“凭证”。协议只规定了它的作用证明你有权限但没有规定它必须长什么样。JWTJSON Web Token是一种具体的技术标准/格式。它定义了令牌内部该如何编码、如何签名、包含哪些字段。 生活化类比Access Token就像是“门票”这个词。它的作用是让你进入游乐园。JWT就像是“印有二维码和防伪水印的纸质票”。这是一种非常流行的门票制作形式。但是“门票”也可以是其他形式比如老式的“打孔硬卡纸”或者“手环”。 它们到底是什么关系1. 包含与被包含的关系并不是所有的 Access Token 都是 JWT但 JWT 经常被用作 Access Token。Access Token 的表现形式通常有两种不透明字符串 (Opaque String)比如4a8f7b2c1d9e。这种令牌本身没有任何意义就像一把随机的钥匙。服务器收到后必须去数据库里查“这把钥匙对应哪个用户过期了吗”传统 Session 模式常用。自包含字符串 (Self-contained)也就是JWT。比如eyJhbG...xxxx.yyyy。服务器收到后不需要查数据库直接通过数学公式验签就能算出它是谁、有没有被篡改。2. 为什么现在大家总把两者混为一谈因为在现代前后端分离、微服务架构的开发中使用 JWT 作为 Access Token 是最主流、最高效的方案。所以很多开发者在日常交流时习惯性地把 Access Token 等同于 JWT但在严谨的技术定义上它们不能划等号。⚖️ 直观对比普通 Access Token vs JWT 格式的 Access Token假设两个用户都要访问“获取订单信息”的接口他们携带的 Access Token 处理方式完全不同特性传统不透明 Access TokenJWT 格式的 Access Token外观示例gAAAABBBBCCCC12345(随机乱码)eyJhbGciOiJ...xxxxx.yyyyy(三段式结构)服务器验证方式查库拿着 Token 去数据库/Redis 查询对应的用户信息。计算本地通过算法解密并验证签名无需查库。性能每次请求都要访问数据库高并发时数据库压力大。纯CPU计算速度极快非常适合分布式系统。包含信息无信息全在服务端数据库里。有Payload 里自带 UserID、角色等信息。注销难度容易直接在数据库删掉这条记录即可。较难因为无状态通常需要配合黑名单或短有效期。 总结你可以这样向别人介绍它们的关系Access Token是为了保护 API 而设立的一道关卡任何能通关的凭证都叫 Access Token。JWT是目前最流行的一种通关凭证样式。它把用户信息直接写在了凭证上并且加了防伪印章让守门员服务器一眼就能识别不用再去翻花名册查数据库。所以在实际工作中当你的领导或同事说“生成一个 Access Token”时大概率他们指的就是“生成一个 JWT 字符串”。Bearer 是什么意思Bearer翻译成中文就是**“持有者”或“携带者”**的意思。在 HTTP 身份验证尤其是使用 JWT 作为 Access Token中它代表了一种非常直接的授权逻辑“谁手里拿着这个令牌Token服务器就认为谁是合法用户。”️ Bearer 的核心含义认票不认人当你在请求头中看到Authorization: Bearer 你的JWT时它的意思是“你好服务器我是这个令牌的持有者Bearer。我把这张门票交给你看请允许我访问受保护的资源。”服务器收到后不会去核实“你是不是最初登录的那个人”比如不会让你再输一次密码或做人脸识别而是只检查你手里的这张票据JWT是否真实、是否在有效期内。只要票据是真的服务器就放行。 标准格式在 HTTP 请求的 Header 中Bearer 认证的标准写法是固定的Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...Authorization这是标准的 HTTP 请求头字段名表示“我要进行身份验证”。Bearer这是认证方案Scheme告诉服务器“我后面跟着的是一个令牌请你按‘持有者’的规则来验证它”。eyJhb…这就是你的 JWTAccess Token。注意Bearer和Token之间必须有一个空格。 为什么要加 “Bearer” 这个词你可能会问直接发 Token 不行吗为什么要多写一个单词这是因为 HTTP 协议支持多种身份验证方式。加上Bearer是为了明确告诉服务器当前使用的是哪种规则。常见的还有BasicAuthorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ通常用于传用户名和密码的 Base64 编码Digest一种比 Basic 更安全的摘要认证方式。所以Bearer就像是一个“暗号前缀”服务器一看到它就知道“哦接下来要处理的是一个 JWT/Access Token我要去验签了。” 安全提醒正因为 Bearer 认证是**“认票不认人”**所以保护你的 Access TokenJWT至关重要如果黑客通过抓包、XSS 攻击等手段窃取了你的 JWT他就可以伪装成你成为合法的 Bearer随意调用你的接口。这也是为什么现代 Web 开发强烈建议使用HTTPS防止半路被抢走票据以及将 Token 存放在HttpOnly Cookie防止被网页里的恶意脚本偷走的原因。如何将Token存放在HttpOnly Cookie将 Token 存放在 HttpOnly Cookie 中是保护用户身份凭证如 JWT免受 XSS跨站脚本攻击的最佳实践。实现这一点的核心原则是Token 绝对不能由前端 JavaScript 来设置或读取必须完全交由后端在服务端通过 HTTP 响应头下发并由浏览器自动管理。下面为你详细拆解从后端到前端的完整落地流程️ 第一步后端设置并下发 HttpOnly Cookie当用户登录成功时后端在返回的 HTTP 响应中通过Set-Cookie响应头将 Token 写入浏览器的 Cookie 中并附加严格的安全属性。核心安全属性说明HttpOnly这是最关键的属性。它禁止前端 JavaScript如document.cookie访问该 Cookie从根本上杜绝了 XSS 窃取 Token 的风险。Secure强制要求该 Cookie 只能在 HTTPS 协议下传输防止在网络传输过程中被中间人窃听。SameSite限制第三方网站发起的请求携带此 Cookie。建议设置为Strict或Lax能有效防御 CSRF跨站请求伪造攻击。Path通常设置为/表示该 Cookie 在整个域名下的所有路径都有效。代码示例以 Node.js/Express 为例app.post(/api/login,(req,res){// 1. 验证用户账号密码...// 2. 生成 JWT Token...consttokengenerateJWT(user);// 3. 将 Token 放入 HttpOnly Cookie 中下发给客户端res.cookie(access_token,token,{httpOnly:true,// 核心JS无法读取防XSSsecure:process.env.NODE_ENVproduction,// 生产环境仅HTTPS传输sameSite:strict,// 严格限制跨站请求防CSRFmaxAge:24*60*60*1000,// 有效期1天path:/// 全站生效});res.status(200).json({message:登录成功});}); 第二步前端正常发送请求跨域需特殊处理一旦后端设置了 HttpOnly Cookie浏览器就会接管后续工作。同域场景当前端和后端部署在同一个域名下时浏览器会在每次向该域名发起 HTTP 请求时自动在请求头中带上这个 Cookie。前端代码无需做任何额外操作直接调用接口即可。跨域场景如果前后端分离且存在跨域例如前端在www.example.com后端 API 在api.example.com你需要在前端的请求库中显式开启“携带凭证”的配置。前端跨域请求配置示例使用 Axiosimportaxiosfromaxios;// 全局配置允许跨域请求自动携带 Cookie (即 HttpOnly Token)axios.defaults.withCredentialstrue;axios.defaults.baseURLhttps://api.example.com;constgetUserInfoasync(){// 浏览器会自动把 access_token 放在 Cookie 请求头中发往后端constresawaitaxios.get(/user/info);returnres.data;}; 第三步后端验证 Token当受保护的接口收到请求时后端直接从请求头的Cookie字段中提取 Token 并进行验签而不是去提取Authorization头。后端验证逻辑示例functionauthMiddleware(req,res,next){// 从请求的 Cookie 中获取 Tokenconsttokenreq.cookies.access_token;if(!token){returnres.status(401).json({message:未提供身份凭证});}try{// 验证 Token 的有效性是否过期、签名是否正确constdecodedverifyJWT(token);req.userdecoded;// 将解析出的用户信息挂载到请求对象上next();}catch(error){returnres.status(401).json({message:身份凭证无效或已过期});}}⚠️ 注意事项与避坑指南绝对不要在 localStorage 中存储敏感 Token很多开发者习惯用localStorage.setItem(token, xxx)但这会让 Token 暴露在document.cookie或全局 JS 环境中一旦发生 XSS 攻击黑客可以轻易盗取你的 Token。CSRF 防护不能少虽然SameSite属性已经能抵御大部分 CSRF 攻击但在涉及修改数据的关键操作如转账、改密时建议后端依然配合传统的 CSRF Token 机制进行双重校验。退出登录的实现由于前端无法删除 HttpOnly Cookie退出登录必须由前端调用后端的登出接口后端再通过Set-Cookie将该 Cookie 清空通常是将maxAge设为 0 或赋一个空值。