构建安全 Web 应用:从用户认证与授权到 JWT 原理解析
构建安全 Web 应用:从用户认证与授权到 JWT 原理解析
开篇引入
在互联网的世界里,用户身份验证与权限控制是任何 Web 应用的基石。从最早的 Cookie+Session,到如今流行的无状态 Token 机制,背后都源自同一目标:确保每一次请求都真实可信、授权受控。伴随单页应用(SPA)、移动端与微服务架构的普及,传统的 Session 存储与管理方式逐渐暴露出跨域困扰、水平扩展受限等痛点。
JSON Web Token(JWT)以其“无状态”“携带自足”的特性应运而生,正被电商、社交、金融等对安全与性能有着严苛要求的场景广泛采用。本文将带你一探:
- 用户认证(Authentication)与授权(Authorization)的本质区别
- 传统 Session 与 JWT 的对比与取舍
- JWT 在底层如何构建、签名与校验
- Python 生态中使用 Django REST Framework 与 FastAPI 实践 JWT
- 刷新令牌、角色管理与安全最佳实践
无论你是刚入门的后端新人,还是负责大规模系统安全的资深工程师,都能在这里找到落地可行的思路与示例。
一、认证 vs 授权:Fundamentals
-
认证(Authentication)
确定“是谁在请求”。常见方式包括用户名/密码、OAuth、短信验证码、第三方登录。 -
授权(Authorization)
确定该身份“可以做什么”。包括角色(Role-Based Access Control,RBAC)与权限(Permission-Based Access Control,PBAC)等细粒度控制。
二者往往协同:先认证用户身份,再根据角色与权限决定访问资源的许可。
二、Session 机制与 JWT 对比
特性 | Session(状态管理) | JWT(无状态 Token) |
---|---|---|
存储位置 | 服务端(内存、数据库、缓存) | 客户端(Cookie、LocalStorage) |
可扩展性 | 需集中存储,多实例需共享或粘性路由 | 天然无状态,易于水平扩展 |
跨域支持 | 跨域 Cookie 与 CSRF 问题需额外处理 | 支持跨域 CORS,只需在 API 端校验头部 |
性能 | 请求需检索存储,I/O 开销 | 仅需本地解码与签名校验,性能更优 |
撤销/失效 | 服务端可直接删除 Session | 无状态难以强制实时失效,需黑名单/版本号 |
三、JWT 工作原理详解
JWT(JSON Web Token)由三部分组成,之间用 .
分隔:
HEADER.PAYLOAD.SIGNATURE
3.1 Header
{"alg": "HS256", // 签名算法"typ": "JWT" // Token 类型
}
- Base64URL 编码后形成第一段。
3.2 Payload(Claims)
常见声明字段(Claim):
iss
:发行者(Issuer)sub
:主题(Subject,一般为用户 ID)aud
:受众(Audience)exp
:过期时间(Expiration Time,Unix 时间戳)iat
:签发时间(Issued At)- 自定义字段:如
role
、permissions
{"sub": "user123","role": "admin","iat": 1628000000,"exp": 1628003600
}
- 同样进行 Base64URL 编码形成第二段。
3.3 Signature
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secretKey
)
- 将 Header 与 Payload 及服务端密钥(
secretKey
)通过 HMAC-SHA256 签名后,Base64URL 编码即得第三段。
3.4 验证流程
- 客户端每次请求将完整 JWT 附加在
Authorization: Bearer <token>
头中。 - 服务端分三步校验:
- Base64URL 解码 Header 与 Payload
- 校验
exp
、nbf
等时间字段 - 使用同样的
secretKey
对前两段重新签名,并与第三段对比
成功则合法,否则认为 Token 无效或被篡改。
四、在 Django 中使用 JWT 实践
我们以 Django REST Framework + djangorestframework-simplejwt 为示例,快速搭建一个登录认证与受保护接口。
4.1 安装与配置
pip install djangorestframework djangorestframework-simplejwt
在 settings.py
中:
INSTALLED_APPS = [# ..."rest_framework",
]REST_FRAMEWORK = {"DEFAULT_AUTHENTICATION_CLASSES": ("rest_framework_simplejwt.authentication.JWTAuthentication",),
}from datetime import timedeltaSIMPLE_JWT = {"ACCESS_TOKEN_LIFETIME": timedelta(minutes=15