JWT 登录认证:原理与 Flask 实现
# JWT 登录认证:原理与 Flask 实现
前后端分离项目里,JWT(JSON Web Token)几乎是最常用的登录认证方案。这篇文章讲清楚它的原理,再给一版可以直接抄的 Flask 实现。
## 1. 为什么要用 JWT,而不是传统 Session
传统的 Session 认证依赖服务端存储会话信息(比如存在 Redis 或内存里),每次请求都要拿 Session ID 去查一次,服务端要维护状态,多台服务器之间还得做 Session 共享。
JWT 是一种**无状态**方案:服务端把用户信息签名后打包成一个 token 返回给客户端,之后客户端每次请求带上这个 token,服务端只需要验证签名,不用查任何存储。天然适合分布式、前后端分离的场景。
## 2. JWT 的结构
一个 JWT 由三部分组成,用 `.` 分隔:
```
header.payload.signature
```
- **header**:声明算法类型,比如 `HS256`
- **payload**:存放实际数据,比如用户 id、过期时间
- **signature**:对前两部分用密钥签名,防止内容被篡改
注意:payload 只是 Base64 编码,**不是加密**,不要在里面塞密码这类敏感信息。
## 3. Flask 实现
安装依赖:
```bash
pip install pyjwt
```
生成 token:
```python
import jwt
import datetime
SECRET_KEY = 'your-secret-key' # 建议放到环境变量里
def generate_token(user_id):
payload = {
'user_id': user_id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=2),
'iat': datetime.datetime.utcnow(),
}
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
```
登录接口里调用:
```python
@app.route('/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
user = User.query.filter_by(username=username).first()
if not user or not user.check_password(password):
return jsonify({'msg': '用户名或密码错误'}), 401
token = generate_token(user.user_id)
return jsonify({'token': token})
```
## 4. 用装饰器保护接口
写一个装饰器统一校验 token,避免每个接口都重复写验证逻辑:
```python
from functools import wraps
from flask import request, jsonify, g
def login_required(f):
@wraps(f)
def decorated(*args, **kwargs):
auth_header = request.headers.get('Authorization', '')
if not auth_header.startswith('Bearer '):
return jsonify({'msg': '缺少 token'}), 401
token = auth_header.split(' ')[1]
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
g.user_id = payload['user_id']
except jwt.ExpiredSignatureError:
return jsonify({'msg': 'token 已过期'}), 401
except jwt.InvalidTokenError:
return jsonify({'msg': '无效的 token'}), 401
return f(*args, **kwargs)
return decorated
@app.route('/profile')
@login_required
def profile():
user = User.query.get(g.user_id)
return jsonify(user.to_dict())
```
## 5. 常见的几个坑
- **token 过期时间设太长**:一旦泄露,攻击者能用很久。一般 access token 设置成较短时间(比如几十分钟到几小时),配合 refresh token 做续期。
- **退出登录做不了**:JWT 本身是无状态的,服务端没法主动"作废"一个 token。如果要支持强制退出登录,通常需要引入黑名单机制(比如把已注销的 token 存进 Redis,验证时先查一下)。
- **payload 塞了敏感信息**:payload 任何人都能 Base64 解码看到,不要放密码、手机号这类信息。
## 小结
JWT 的核心思路是"用签名代替服务端存储状态",天然适合前后端分离和分布式部署。用的时候记得控制好过期时间,需要强制下线功能的话再配合黑名单方案,基本就能覆盖大多数登录认证场景。
评论区