Become a sponsor

本章概要
登录 -> Token 获取 -> 权限校验 -> Token 刷新 -> 退出登录的完整认证链路实战说明。
本章将登录 -> Token 获取 -> 权限校验 -> Token 刷新 -> 退出登录的完整认证链路串联说明,帮助开发者理解端到端的认证机制。
+----------+ 1获取验证码 +----------+
| | -----------------> | |
| 前端 | | 后端 |
| Vue3 | 2登录(账号+密码+验证码) | Django |
| | -----------------> | |
| | 3返回 Token | |
| | <------------------ | |
| | | |
| | 4携带 Token 请求 | |
| | -----------------> | |
| | 5返回业务数据 | |
| | <------------------ | |
| | | |
| | 6刷新 Token | |
| | -----------------> | |
| | 7返回新 Token | |
| | <------------------ | |
| | | |
| | 8退出登录 | |
| | -----------------> | |
+----------+ +----------+登录前需先获取图形验证码,防止暴力破解。
GET /login/captcha{
"code": 0,
"msg": "操作成功",
"data": {
"captcha": "data:image/png;base64,iVBORw0KGgo...",
"key": 1
},
"ok": true
}captcha:Base64 编码的验证码图片,前端直接赋值给 <img> 的 srckey:验证码记录 ID,登录时需回传# application/login/views.py
from captcha.models import CaptchaStore
from captcha.views import captcha_image
class CaptchaView(View):
def get(self, request):
# 生成验证码密钥
hashkey = CaptchaStore.generate_key()
id_key = CaptchaStore.objects.filter(hashkey=hashkey).first().id
# 生成验证码图片并转 Base64
image = captcha_image(request, hashkey)
base64_image = base64.b64encode(image.content)
img_str = "data:image/png;base64," + base64_image.decode("utf-8")
return R.ok(idkey=id_key, data={"captcha": img_str, "key": id_key})验证码通过 django-simple-captcha 生成,存储在数据库中。
携带账号、密码、验证码和 Key 发起登录请求。
POST /login/login
Content-Type: application/json
{
"username": "admin",
"password": "123456",
"code": "a3Kp",
"key": 1
}{
"code": 0,
"msg": "登录成功",
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"must_change_password": false
},
"ok": true
}access_token:JWT 令牌,后续请求需携带must_change_password:是否需要修改密码1. 从请求体获取 username、password、code、key
2. 验证码校验(比对数据库中的验证码记录)
- 不正确 -> 返回失败
3. 查询用户(username + status=1)
- 不存在 -> 返回失败
4. 比对密码(bcrypt 双重加盐验证)
- 不匹配 -> 返回失败
5. 登录成功
- 生成 JWT Token
- 记录登录日志
- 返回 Token登录成功后,前端将 Token 存入 localStorage,后续所有请求自动携带。
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...请求进入 -> check_login 装饰器
+-- 提取 Authorization 头(request.META['HTTP_AUTHORIZATION'])
+-- 移除 "Bearer " 前缀
+-- parse_payload() 解析 JWT Token
| +-- 验证签名(HS256 + JWT_SALT)
| +-- 验证过期时间
| +-- 检查黑名单(Redis)
+-- 验证通过 -> 执行原视图函数
+-- 验证失败 -> R.failed(code=401, msg=...)# middleware/login_middleware.py
def check_login(func):
def wrapper(request, *args, **kwargs):
ignoreURL = ['/login', '/captcha']
if request.path not in ignoreURL:
access_token = request.headers['Authorization']
access_token = access_token.replace('Bearer ', "")
result = parse_payload(access_token)
if result['code'] != 0:
return R.failed(code=401, msg=result['msg'])
return func(request, *args, **kwargs)
return wrapper每个业务接口通过 PermissionRequired 混入类校验权限。
请求到达 -> PermissionRequired.has_permission()
+-- 用户 ID = 1(admin)-> 跳过权限校验,直接放行
+-- 其他用户
+-- 查询用户权限列表(menu.services.get_user_permissions)
+-- 匹配权限字符串
| +-- 匹配 -> 放行
| +-- 不匹配 -> 返回 "暂无操作权限"sys:{module}:{action}示例:sys:user:add、sys:role:page、sys:article:delete
Token 支持无感刷新,刷新时生成新令牌。
POST /login/refreshToken
Authorization: Bearer <old_token>1. 验证原令牌有效性
2. 移除 JWT 标准字段(exp/iat/nbf),保留业务数据
3. 生成新令牌
4. 返回新令牌退出时将当前 Token 加入 Redis 黑名单,使其立即失效。
GET /login/logout
Authorization: Bearer <token>{
"code": 0,
"msg": "注销成功",
"data": null,
"ok": true
}1. 提取当前 Token
2. 计算 Token 的 SHA256 指纹
3. 存入 Redis 黑名单(TTL = Token 剩余有效期)
4. 记录退出日志
5. 返回成功token:blacklist:{sha256_fingerprint}| 环境变量 | 默认值 | 说明 |
|---|---|---|
JWT_SALT | 内置默认密钥 | JWT 签名密钥,建议至少 32 字节 |
DEFAULT_TIMEOUT_MINUTES | 20 | Token 过期时间(分钟) |
认证流程覆盖了从获取验证码到退出登录的完整链路,核心安全机制包括: