JWT 认证与鉴权实战:结构、签名与安全设计

举报
yd_268144123 发表于 2026/11/07 09:46:37 2026/11/07
【摘要】 一、为什么用 JWT传统 Session 方案把登录状态存在服务端,靠 Cookie 里的 sessionId 去查。服务一多、要水平扩展时,就得引入共享存储(Redis)来同步会话,或者做会话粘滞,比较麻烦。JWT(JSON Web Token)把“用户是谁、有什么权限”编码进一个自带签名的令牌里,服务端无需存会话,只要用密钥验签就能确认令牌没被篡改。这让它在无状态 API、微服务、前后...

一、为什么用 JWT

传统 Session 方案把登录状态存在服务端,靠 Cookie 里的 sessionId 去查。服务一多、要水平扩展时,就得引入共享存储(Redis)来同步会话,或者做会话粘滞,比较麻烦。

JWT(JSON Web Token)把“用户是谁、有什么权限”编码进一个自带签名的令牌里,服务端无需存会话,只要用密钥验签就能确认令牌没被篡改。这让它在无状态 API、微服务、前后端分离场景里非常流行。

二、JWT 的结构

一个 JWT 由三段用 . 连接的 Base64Url 字符串组成:

eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ . 签名
      Header                     Payload                    Signature
  • Header:声明签名算法(如 HS256、RS256)。
  • Payload:承载 claims,如 sub(用户)、exp(过期时间)、role(角色)、自定义字段。
  • Signature:对 header.payload 用密钥/私钥签名,防止篡改。

关键认知:Payload 只是 Base64 编码,不是加密,任何人都能解开看内容。所以绝不能在 JWT 里放密码、身份证等敏感信息。

三、用 Python 生成与校验

import jwt, time

SECRET = "your-256-bit-secret"

def issue_token(user_id, role):
    payload = {
        "sub": user_id,
        "role": role,
        "iat": int(time.time()),
        "exp": int(time.time()) + 3600,   # 1 小时后过期
    }
    return jwt.encode(payload, SECRET, algorithm="HS256")

def verify_token(token):
    try:
        return jwt.decode(token, SECRET, algorithms=["HS256"])
    except jwt.ExpiredSignatureError:
        raise ValueError("token 已过期")
    except jwt.InvalidTokenError:
        raise ValueError("token 无效")

校验时必须显式指定算法白名单(algorithms=["HS256"]),否则存在 “alg=none / 算法混淆” 攻击风险。

四、HS256 还是 RS256

  • HS256(对称):签发和校验用同一密钥,简单高效,适合单体或可信内部服务。
  • RS256(非对称):私钥签名、公钥验签。签发方只在一处持有私钥,各资源服务只拿公钥验签,适合微服务/多方场景,私钥泄露面更小。

原则:签名密钥绝不能下发到客户端;微服务架构优先 RS256,让验证方只持有公钥。

五、刷新令牌与安全设计

JWT 一旦签发,在过期前服务端难以主动作废(无状态的好处也是麻烦)。常见做法:

  1. 短寿命 Access Token(如 15 分钟)+ 长寿命 Refresh Token(如 7 天)。
  2. Access 过期后用 Refresh 换新,Refresh 存服务端可撤销(如 Redis 白名单)。
  3. 登出/封禁:把要作废的 token(或 jti)加入黑名单,校验时查一下黑名单。
  4. Refresh Token 轮换:每次刷新换发新的 refresh,旧的作废,降低被盗用风险。

六、必须防的几个坑

  1. 存哪:放 localStorage 易被 XSS 读取;更稳妥是 HttpOnly + Secure Cookie(防 JS 读取),并配 SameSite 防 CSRF。
  2. 不要塞敏感数据:Payload 明文可读,别放密码/密钥。
  3. 一定要设 exp 并校验;不要接受永不过期的 token。
  4. 算法白名单:拒绝 alg=none,固定期望算法。
  5. 传输必须 HTTPS:否则 token 在链路上被截获。
  6. 签名密钥要够强:HS256 密钥太短可被暴力破解。

七、一个校验中间件示例

from functools import wraps
from flask import request, jsonify

def auth_required(roles=None):
    def deco(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            auth = request.headers.get("Authorization", "")
            if not auth.startswith("Bearer "):
                return jsonify(msg="缺少令牌"), 401
            try:
                claims = verify_token(auth[7:])
            except ValueError as e:
                return jsonify(msg=str(e)), 401
            if roles and claims.get("role") not in roles:
                return jsonify(msg="无权限"), 403
            request.user = claims
            return fn(*args, **kwargs)
        return wrapper
    return deco

八、JWT vs Session 怎么选

  • 无状态 API、微服务、跨域前后端分离、移动端:JWT 合适。
  • 需要强会话控制、即时踢下线、单体内应用:传统 Session + 服务端存储更简单可控。
  • 二者也可结合:JWT 做短期身份凭证,服务端保留少量可撤销状态。

九、总结

JWT 的核心是自包含 + 签名防篡改 + 无状态校验。用好它的关键是:Payload 不放敏感信息、算法固定白名单、必备 exp、短寿命 Access + 可撤销 Refresh、令牌存 HttpOnly Cookie、全程 HTTPS。记住这些,就能在享受无状态便利的同时避开 JWT 常见的安全坑。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。