React如何对接Django后端实现用户登录、状态维护与登出
Django + React 前后端分离登录/登出实现方案
核心逻辑说明
Django原生登录的底层逻辑非常简单,整个流程和模板渲染模式没有本质区别,核心只有三步:
- 校验传入的用户名、密码是否匹配数据库用户
- 校验通过后生成服务端session,将
sessionid写入响应头的Set-Cookie字段 - 后续所有携带合法
sessionid的请求,Django中间件会自动解析出对应用户,直接通过request.user获取用户实例、request.user.is_authenticated判断登录状态,不需要额外开发
前后端分离模式下不需要替换成JWT等其他方案,只需要处理跨域场景下的Cookie携带、CSRF校验两个问题即可,完全复用Django原生的认证能力。
后端(Django)配置
1. 安装并配置跨域依赖
先安装django-cors-headers处理跨域请求:
pip install django-cors-headers
修改settings.py配置:
INSTALLED_APPS = [ # 原有应用配置保留 'corsheaders', ] MIDDLEWARE = [ # 该中间件必须放在最顶部,优先于CommonMiddleware 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # 原有其他中间件保留 ] # 允许前端本地开发地址跨域 CORS_ALLOWED_ORIGINS = [ "http://127.0.0.1:3000", "http://localhost:3000", ] # 允许跨域请求携带Cookie CORS_ALLOW_CREDENTIALS = True # 配置CSRF信任的前端源,避免POST请求被CSRF防护拦截 CSRF_TRUSTED_ORIGINS = [ "http://127.0.0.1:3000", "http://localhost:3000", ]
2. 编写认证相关接口
废弃原有模板渲染的LoginView,直接编写返回JSON的接口:
# users/views.py from django.contrib.auth import authenticate, login, logout from django.http import JsonResponse from django.middleware.csrf import get_token from django.views.decorators.csrf import ensure_csrf_cookie import json # 获取CSRF Token接口,前端初始化时调用 @ensure_csrf_cookie def get_csrf_token(request): return JsonResponse({"csrfToken": get_token(request)}) # 登录接口 def login_api(request): if request.method != "POST": return JsonResponse({"code": 405, "msg": "请求方法不允许"}, status=405) request_data = json.loads(request.body) username = request_data.get("username", "").strip() password = request_data.get("password", "") # 校验账号密码 user = authenticate(request, username=username, password=password) if not user: return JsonResponse({"code": 400, "msg": "用户名或密码错误"}, status=400) # 调用Django原生login方法,写入session login(request, user) # 登录成功直接返回用户基本信息,sessionid会自动通过Set-Cookie写入浏览器 return JsonResponse({ "code": 200, "msg": "登录成功", "data": { "id": user.id, "username": user.username } }) # 登出接口 def logout_api(request): logout(request) return JsonResponse({"code": 200, "msg": "登出成功"}) # 获取当前登录用户信息接口 def get_current_user(request): if not request.user.is_authenticated: return JsonResponse({"code": 401, "msg": "未登录"}, status=401) return JsonResponse({ "code": 200, "data": { "id": request.user.id, "username": request.user.username } })
3. 配置路由
# users/urls.py from django.urls import path from . import views urlpatterns = [ path('api/csrf/', views.get_csrf_token, name='api-csrf'), path('api/login/', views.login_api, name='api-login'), path('api/logout/', views.logout_api, name='api-logout'), path('api/user/info/', views.get_current_user, name='api-user-info'), # 原有其他业务接口保留 ]
前端(React)对接
所有请求必须开启withCredentials配置,否则跨域场景下Cookie不会被携带,后端无法识别登录状态。
1. 封装请求实例
以axios为例:
// utils/request.js import axios from 'axios' const service = axios.create({ baseURL: 'http://127.0.0.1:8000', // 核心配置:跨域请求携带Cookie withCredentials: true }) // 请求拦截器自动注入CSRF Token service.interceptors.request.use(config => { const csrfToken = document.cookie .split('; ') .find(row => row.startsWith('csrftoken=')) ?.split('=')[1] if (csrfToken) { config.headers['X-CSRFToken'] = csrfToken } return config }) // 响应拦截器统一处理未登录场景 service.interceptors.response.use( response => response.data, error => { if (error.response.status === 401) { // 未登录时清空用户状态,跳转登录页 window.location.href = '/login' } return Promise.reject(error) } ) export default service
2. 应用初始化校验登录状态
应用加载时先获取CSRF Cookie,再拉取当前用户信息,判断是否已登录:
// App.jsx import { useEffect, useState } from 'react' import request from './utils/request' import LoginPage from './pages/Login' import HomePage from './pages/Home' function App() { const [user, setUser] = useState(null) const [loading, setLoading] = useState(true) useEffect(() => { const initAuth = async () => { try { // 初始化CSRF Cookie await request.get('/api/csrf/') // 拉取当前登录用户信息 const res = await request.get('/api/user/info/') setUser(res.data) } catch (err) { setUser(null) } finally { setLoading(false) } } initAuth() }, []) if (loading) return <div>加载中...</div> return user ? <HomePage user={user} setUser={setUser} /> : <LoginPage setUser={setUser} /> } export default App
3. 登录页实现
// pages/Login.jsx import { useState } from 'react' import request from '../utils/request' export default function LoginPage({ setUser }) { const [formData, setFormData] = useState({username: '', password: ''}) const [errorMsg, setErrorMsg] = useState('') const handleSubmit = async (e) => { e.preventDefault() setErrorMsg('') try { const res = await request.post('/api/login/', formData) // 登录成功后将用户信息存入全局状态 setUser(res.data) } catch (err) { setErrorMsg(err.response.data.msg || '登录失败,请重试') } } return ( <form onSubmit={handleSubmit}> {errorMsg && <p className="error">{errorMsg}</p>} <div> <label>用户名</label> <input type="text" value={formData.username} onChange={e => setFormData({...formData, username: e.target.value})} /> </div> <div> <label>密码</label> <input type="password" value={formData.password} onChange={e => setFormData({...formData, password: e.target.value})} /> </div> <button type="submit">登录</button> </form> ) }
4. 登出功能实现
在需要触发登出的位置添加按钮,调用登出接口即可:
// 组件内登出逻辑 const handleLogout = async () => { await request.post('/api/logout/') setUser(null) }
常见问题说明
- 密码明文传输问题:本地开发阶段无需额外处理,上线时给前后端配置HTTPS即可,传输层会自动加密所有请求数据。自行在前端做密码加密没有实际防护意义,中间人截获加密后的密文依然可以重放请求,HTTPS是成本最低、最可靠的方案。
- 登录状态维护:不需要额外在localStorage等位置存储令牌,
sessionid会由浏览器自动保存在Cookie中,只要请求开启了withCredentials,后端会自动识别用户身份。前端只需要把用户信息存在全局状态(Context、Redux、Zustand均可),页面刷新时重新调用/api/user/info拉取用户信息即可。 - 接口权限控制:后端视图可以直接复用Django自带的
@login_required装饰器,未登录的请求会自动返回错误,前端在响应拦截器统一处理跳转逻辑即可,和原有模板模式的权限逻辑完全一致。
内容的提问来源于stack exchange,提问作者LtCol_Davenport
相关产品推荐
相关产品推荐

