Flask 示例:设置 HttpOnly Cookie 存储 JWT
from flask import Flask, make_response, request, jsonify
import jwt
import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
@app.route('/login', methods=['POST'])
def login():
# 模拟登录成功后的用户信息
user_id = 123
token = jwt.encode(
{'user_id': user_id, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)},
app.config['SECRET_KEY'],
algorithm='HS256'
)
# 创建响应对象
resp = make_response(jsonify({'message': '登录成功'}))
# 设置 HttpOnly Cookie
resp.set_cookie(
'access_token', # Cookie 名称
token, # Cookie 值(JWT)
httponly=True, # 禁止 JS 访问,防 XSS
secure=False, # 生产环境应设为 True(仅 HTTPS)
samesite='Lax', # 防 CSRF,推荐 Lax 或 Strict
max_age=3600 # 有效期 1 小时
)
return resp
@app.route('/protected')
def protected():
token = request.cookies.get('access_token')
if not token:
return jsonify({'error': '未授权'}), 401
try:
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
return jsonify({'user_id': payload['user_id']})
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Token 已过期'}), 401
解析亮点
-
set_cookie()
设置了 HttpOnly 属性,防止 JS 访问。 -
secure=False
仅用于开发环境,生产环境必须启用 HTTPS。 -
samesite='Lax'
防止跨站请求伪造(CSRF)。 -
request.cookies.get()
用于读取 Cookie 中的 Token。
Django 示例:设置 HttpOnly Cookie 存储 JWT
from rest_framework.views import APIView
from rest_framework.response import Response
from django.conf import settings
from django.utils import timezone
import jwt
class LoginView(APIView):
def post(self, request):
user_id = 123 # 模拟登录成功
payload = {
'user_id': user_id,
'exp': timezone.now() + timezone.timedelta(hours=1)
}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256')
response = Response({'message': '登录成功'})
# 设置 HttpOnly Cookie
response.set_cookie(
key='access_token',
value=token,
httponly=True,
secure=not settings.DEBUG,
samesite='Lax',
max_age=3600
)
return response
class ProtectedView(APIView):
def get(self, request):
token = request.COOKIES.get('access_token')
if not token:
return Response({'error': '未授权'}, status=401)
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
return Response({'user_id': payload['user_id']})
except jwt.ExpiredSignatureError:
return Response({'error': 'Token 已过期'}, status=401)
解析亮点
-
response.set_cookie()
设置 HttpOnly Cookie。 -
secure=not settings.DEBUG
:开发环境允许 HTTP,生产强制 HTTPS。 -
request.COOKIES.get()
读取 Cookie 中的 Token。 -
使用
jwt.decode()
验证 Token 合法性和过期时间。
安全建议(适用于 Flask 和 Django)
项目 | 建议配置 |
---|---|
httponly | ✅ 必须开启,防止 XSS |
secure | ✅ 生产环境必须开启 HTTPS |
samesite | ✅ 推荐 'Lax' 或 'Strict' |
max_age | 根据业务设置有效期 |
Token刷新 | 建议使用 Refresh Token + 自动刷新机制 |
SameSite 属性作用
它是浏览器用来防止 CSRF(跨站请求伪造)攻击 的机制之一。通过限制 Cookie 在跨站请求中的发送行为,提升安全性。
SameSite=Lax(宽松模式)
-
✅ 允许跨站 GET 请求携带 Cookie
-
比如:点击链接跳转、打开新窗口、GET 表单提交
-
-
❌ 禁止跨站 POST、PUT、DELETE 等非安全方法携带 Cookie
-
比如:AJAX 请求、表单自动提交等
-
适合场景:
-
普通网站登录状态维持
-
用户从其他网站跳转访问你的网站时仍能保持登录
SameSite=Strict(严格模式)
-
❌ 所有跨站请求都不携带 Cookie
-
无论是 GET 还是 POST,只要是跨站就不带 Cookie
-
-
✅ 只有在用户直接访问你的网站(同源)时才会带 Cookie
适合场景:
-
高安全性要求的系统(如银行后台、管理系统)
-
不希望任何第三方页面能触发带 Cookie 的请求
举个例子
假设你已经登录了 example.com
:
场景 | Lax | Strict |
---|---|---|
你点击一个链接跳转到 example.com | ✅ Cookie 会带上 | ✅ Cookie 会带上 |
其他网站通过表单 POST 到 example.com | ❌ 不带 Cookie | ❌ 不带 Cookie |
其他网站通过 AJAX 请求 example.com | ❌ 不带 Cookie | ❌ 不带 Cookie |
总结建议
属性 | 安全性 | 用户体验 | 推荐用途 |
---|---|---|---|
Strict | 高 | 差(容易掉登录) | 内部系统、后台管理 |
Lax | 中 | 好 | 普通网站、博客、商城 |
如果你正在做一个后台管理系统,Strict
是更安全的选择;如果你希望用户从其他页面跳转后仍保持登录状态,Lax
更合适。