做前后端分离、写API,登录用JWT几乎成了默认选择。但JWT到底是什么、为什么用三段、签名怎么验、和Session差在哪,很多人停留在会用不懂原理。真出安全问题排查都费劲。
JWT的三段结构
JWT全称JSON Web Token,就是一段字符串,格式是header.payload.signature,三部分用点分隔。比如eyJhbGc.eyJzdW.signature这种。
第一段header,是JSON对象经过Base64URL编码。内容是token的类型和签名算法。比如{typ:JWT, alg:HS256}编码后是eyJhbGc。alg字段指定用什么算法签名,常见的有HS256(HMAC SHA256)和RS256(RSA SHA256)。
第二段payload,也是JSON经过Base64URL编码。里面放声明claims,就是用户身份信息。比如{sub:123, name:张三, exp:1700000000}。sub是用户ID,name是用户名,exp是过期时间。注意payload只是编码不是加密,别放密码这种敏感信息。
第三段signature,签名。把header和payload用点拼起来,用密钥按header指定的算法做哈希。比如HS256就是HMAC-SHA256,密钥是你服务器存的secret。签名的作用是防篡改,任何一段被改了签名对不上。
JWT的验证流程
用户登录成功后,服务器生成JWT返回给客户端。客户端存起来(通常存localStorage或cookie),后续每次请求带上这个token(通常放Authorization头,格式Bearer token)。
服务器收到请求后怎么验证?四步。第一步取出token按点拆成三段。第二步Base64URL解码header和payload看内容。第三步用header里指定的算法和你服务器的密钥,对header.payload重新算签名,和token第三段比对,一致说明没被篡改。第四步检查payload里的exp字段,过期了拒绝。
这个流程的关键在签名验证。签名是服务端用密钥算的,客户端没有密钥改不了。客户端改了payload(比如把sub改成立管理员),重新Base64编码后签名对不上,服务器拒绝。这就是JWT防篡改的原理。
JWT和Session的区别
传统的Session认证:用户登录后服务器存Session(内存或Redis),发个SessionID给客户端存cookie。每次请求带SessionID,服务器查Session找到用户。
JWT认证:用户登录后服务器生成JWT返回,服务器不存任何状态。每次请求带JWT,服务器验证签名和过期即可识别用户。
核心区别是状态存哪。Session状态存服务器,JWT状态存客户端。这导致几个特性差异。
第一扩展性。多台服务器时Session要共享(存Redis或黏性会话),JWT不用因为服务器无状态任何一台都能验。所以微服务架构常用JWT。
第二主动失效。Session好办服务器删了就失效。JWT难因为服务器没存,token签发了到过期前一直有效。想主动让JWT失效需要额外机制:黑名单(存Redis记失效token)、短有效期加刷新token、版本号校验等。
第三安全性。SessionID是随机串不含信息,泄露了只能冒充不能看到内容。JWT的payload客户端能解码(Base64不是加密),所以别放敏感信息。
JWT的安全要点
第一密钥要强。HS256的密钥要足够长且随机,别用secret这种弱密钥。密钥泄露等于所有token可被伪造。建议密钥至少32位随机串,定期轮换。
第二payload别放敏感信息。payload是Base64编码不是加密,任何人能解。用户ID、角色这种可以放,密码、手机号、身份证这些别放。
第三设置合理过期时间。别为了省事设个一年有效期,token泄露了窗口太大。常见做法是access_token有效期15分钟到2小时,配refresh_token有效期7到30天。access_token过期了用refresh_token换新的。
第四HTTPS传输。JWT在传输中被截获就能冒用,必须HTTPS。HTTP下别用JWT。
第五防重放攻击。把token和客户端IP或设备指纹绑定,或者用nonce机制。否则token泄露后任何地方都能用。
HS256和RS256怎么选
HS256是对称加密,签发和验证用同一个密钥。简单,但密钥要在所有验证方共享。微服务多服务都要验证token时,密钥分发是风险。
RS256是非对称加密,私钥签发公钥验证。签发方(认证服务)持私钥,验证方(业务服务)持公钥。公钥泄露无所谓反正只能验不能签。多服务场景RS256更安全。
选择标准:单体应用或服务少用HS256够用。微服务多服务验证用RS256。认证中心统一签发,业务服务用公钥验证,密钥管理更安全。
几个常被问的问题
【JWT被劫持了怎么办?】立即让该用户token失效。常用方法是服务端维护一个token版本号或黑名单,用户登出或异常时把token加入Redis黑名单,每次验证时检查。或者改密钥让所有token失效(影响所有用户慎用)。
【JWT存localStorage还是cookie?】各有利弊。localStorage容量大不自动发送,但要手动加Authorization头,且容易受XSS攻击。cookie自动随请求发送,但受CSRF攻击。常见做法存httpOnly cookie防XSS读取,配合SameSite属性防CSRF。
【JWT能撤销吗?】标准JWT不能撤销因为无状态。要撤销必须引入状态:黑名单存Redis,或token版本号存数据库,每次验证查一下。这就部分失去了JWT无状态的优势,所以高安全场景还是要考虑Session。
JWT用对很顺手用错全是坑。核心记住几点:签名防篡改、payload别放敏感信息、短有效期加refresh、必须HTTPS。搞清这些,JWT是比Session更适合现代Web架构的认证方案。但如果你需要主动失效或会话管理,别硬套JWT,老老实实用Session加Redis可能更简单。