使用django写后端,认证系统使用了内存中的令牌存储(authentication.py中的user_tokens字典)。
from secrets import token_hex
from .models import User
# Create a custom token generation function
def generate_token():
return token_hex(20) # Generate a 40-character hex token
# Create an in-memory token storage (for demonstration)
# In production, you would use a database model for this
user_tokens = {} # user_id: token
# Custom authentication for DRF views
class CustomTokenAuthentication:
def authenticate(self, request):
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Token '):
return None
token = auth_header.split(' ')[1]
# Find user with this token
for user_id, user_token in user_tokens.items():
if user_token == token:
try:
user = User.objects.get(id=user_id)
return (user, token)
except User.DoesNotExist:
return None
return None
def authenticate_header(self, request):
return 'Token'
当使用Gunicorn并配置多个工作进程时(workers = multiprocessing.cpu_count() * 2 + 1),每个工作进程都拥有独立的内存空间和自己的user_tokens字典副本。
这就导致:
用户登录时,其令牌仅存储在某个工作进程的内存中。
而后续请求可能会被路由到另一个没有该令牌内存记录的工作进程。
这将导致认证失败,从而触发前端api.js中的拦截器清除localStorage中的令牌。
import axios from 'axios'
// 创建一个axios实例
const apiClient = axios.create({
baseURL: '/api',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
// 添加超时设置,避免请求挂起过长时间
timeout: 10000
})
// 请求拦截器 - 添加认证token
apiClient.interceptors.request.use(
config => {
// 每次请求时重新从localStorage获取token,确保使用最新token
const token = localStorage.getItem('upload_token')
if (token) {
config.headers.Authorization = `Token ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器 - 全局错误处理
apiClient.interceptors.response.use(
response => {
return response
},
error => {
console.log('API请求错误:', error.response)
// 处理401未授权错误
if (error.response && error.response.status === 401) {
// 可以在这里处理自动登出逻辑
console.log('认证失败,清除令牌')
localStorage.removeItem('upload_token')
localStorage.removeItem('upload_user')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
// 简化的API请求方法
const optimizedApiClient = {
get: (url, config = {}) => apiClient.get(url, config),
post: (url, data, config = {}) => apiClient.post(url, data, config),
put: (url, data, config = {}) => apiClient.put(url, data, config),
delete: (url, config = {}) => apiClient.delete(url, config)
}
export default optimizedApiClient
导致用户登录-》进入其他页面-》重导到登录页反复横跳
处理:
临时处理:
将gunicorn改成单线程
长期处理:
建立token存储表单,采用持久化令牌存储方案:将令牌存入数据库表中,而非内存字典
确保所有工作进程都能访问同一份令牌数据