基于Django与JWT实现移动端用户永久登录及Token自动刷新方案咨询
Hey there! Let's walk through how to implement this seamless auto-refresh functionality for your Django + JWT mobile app—this is a super common pattern and totally achievable with the right setup.
The key here is to split your JWT tokens into two types:
- Access Token: Short-lived (you want 1 month here) for authenticating API requests
- Refresh Token: Longer-lived, used to get a new Access Token (and a new Refresh Token) when the old one expires, without requiring the user to re-enter credentials.
We'll use djangorestframework-simplejwt—the most popular JWT package for Django—to handle the heavy lifting.
Step 1: Set Up Django + SimpleJWT
First, install the package and configure it in your project:
Install Dependencies
pip install djangorestframework-simplejwt django-redis
(We need django-redis for token blacklisting, which prevents old refresh tokens from being abused.)
Configure Settings.py
Add these settings to enable token rotation and blacklisting:
from datetime import timedelta # ... rest of your settings INSTALLED_APPS = [ # ... existing apps 'rest_framework', 'rest_framework_simplejwt', 'rest_framework_simplejwt.token_blacklist', # For token blacklisting ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ) } SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(days=30), # Your 1-month requirement 'REFRESH_TOKEN_LIFETIME': timedelta(days=180), # 6-month refresh token (adjust as needed) 'ROTATE_REFRESH_TOKENS': True, # Generate a new refresh token every time you refresh 'BLACKLIST_AFTER_ROTATION': True, # Mark old refresh tokens as invalid 'ALGORITHM': 'HS256', 'SIGNING_KEY': SECRET_KEY, 'AUTH_HEADER_TYPES': ('Bearer',), # Configure Redis for blacklist storage 'BLACKLIST_STORAGE': 'django_redis.cache.RedisCache', 'BLACKLIST_STORAGE_OPTIONS': { 'LOCATION': 'redis://127.0.0.1:6379/1', # Update to your Redis instance }, }
Add URLs
Include the built-in token endpoints in your urls.py:
from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, ) urlpatterns = [ # ... your existing URLs path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), ]
These endpoints handle initial login (returning access/refresh tokens) and token refreshes.
Step 2: Frontend Auto-Refresh Logic
Your mobile app needs to manage the tokens and handle auto-refreshing. Here's how to approach it (example using React Native, but the logic applies to any mobile framework):
Store Tokens Securely
Never store tokens in plaintext! Use your platform's secure storage:
- iOS: Keychain
- Android: Keystore
- Cross-platform: Libraries like
react-native-keychainorexpo-secure-store
Implement Auto-Refresh Logic
You have two strategies to choose from (or combine both):
1. Proactive Refresh (Recommended)
Check if the access token is about to expire, and refresh it before it does:
import jwtDecode from 'jwt-decode'; import * as SecureStore from 'expo-secure-store'; // Check if token is within 5 minutes of expiring const isTokenExpiringSoon = (token) => { if (!token) return true; const decoded = jwtDecode(token); const currentTime = Date.now() / 1000; return decoded.exp - currentTime < 300; // 5 minutes in seconds }; // Refresh tokens and update secure storage const refreshTokens = async () => { const refreshToken = await SecureStore.getItemAsync('refreshToken'); try { const response = await fetch('https://your-api-domain/api/token/refresh/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh: refreshToken }), }); if (response.ok) { const { access, refresh } = await response.json(); await SecureStore.setItemAsync('accessToken', access); await SecureStore.setItemAsync('refreshToken', refresh); // Save new refresh token return access; } else { // Refresh token expired—force user to log in await SecureStore.deleteItemAsync('accessToken'); await SecureStore.deleteItemAsync('refreshToken'); // Navigate to login screen navigation.navigate('Login'); return null; } } catch (err) { console.error('Token refresh failed:', err); return null; } }; // Wrapper for authenticated API calls const fetchWithAuth = async (url, options = {}) => { let accessToken = await SecureStore.getItemAsync('accessToken'); // Refresh if token is expiring soon if (isTokenExpiringSoon(accessToken)) { accessToken = await refreshTokens(); if (!accessToken) return; } const authOptions = { ...options, headers: { ...options.headers, Authorization: `Bearer ${accessToken}`, }, }; return fetch(url, authOptions); };
2. Reactive Refresh
If you miss the proactive window, handle the 401 Unauthorized response from your API by refreshing tokens and retrying the request:
// Modify fetchWithAuth to handle 401s const fetchWithAuth = async (url, options = {}, retry = true) => { let accessToken = await SecureStore.getItemAsync('accessToken'); const authOptions = { ...options, headers: { ...options.headers, Authorization: `Bearer ${accessToken}`, }, }; const response = await fetch(url, authOptions); // If we get a 401 and haven't retried yet if (response.status === 401 && retry) { const newAccessToken = await refreshTokens(); if (newAccessToken) { // Retry the request with the new token return fetchWithAuth(url, options, false); } } return response; };
Step 3: Security Best Practices
- Refresh Token Rotation: By enabling
ROTATE_REFRESH_TOKENSandBLACKLIST_AFTER_ROTATION, every refresh gives you a new refresh token, and the old one is invalidated. This limits damage if a token is stolen. - Secure Storage: Always use platform-native secure storage for tokens—never AsyncStorage or shared preferences.
- Refresh Token Expiry: Don't set refresh tokens to never expire. A 6-month window is reasonable; if the user is active, tokens will keep rotating. If they're inactive for 6 months, they'll need to log in again (a good security balance).
内容的提问来源于stack exchange,提问作者Santhosh

