React调用Django JWT认证接口出现401未授权问题咨询
Django JWT API集成React后401未授权问题解决
问题描述
用Django搭建了基于JWT的身份验证API,单独调用时:登录获取令牌后调用/api/hello/接口正常返回数据。但集成到React应用后出现问题:
- React能正常登录,react-auth-kit也把JWT令牌存在了Cookie里
- 但在Home.js中调用
/api/hello/时,Django返回401未授权错误 - 手动在请求头里加
Authorization: Bearer <token>就能正常请求,不确定这是不是正确的实现方式
核心原因
Django默认的JWTAuthentication只从请求头的Authorization字段解析令牌,不会自动读取Cookie中的令牌。你的React应用虽然把令牌存在了Cookie,但请求时没有把令牌放到Authorization头里,导致Django无法识别用户身份,返回401。
手动加请求头是正确的实现方式吗?
是的,这是JWT身份验证的标准用法。JWT的官方推荐传递方式就是通过Authorization: Bearer <token>请求头,这种方式安全且符合行业规范。
不过可以优化实现,不用每次请求都手动写请求头,下面给出两种方案:
方案1:全局配置Axios自动添加Authorization头
给Axios设置全局请求拦截器,自动从react-auth-kit获取令牌并添加到请求头,避免重复代码。
- 创建Axios实例文件:
// src/utils/axiosInstance.js import axios from 'axios'; import { getAuthToken } from 'react-auth-kit'; const axiosInstance = axios.create({ baseURL: 'http://localhost:8000/api/', withCredentials: true, }); // 请求拦截器:自动添加Authorization头 axiosInstance.interceptors.request.use( (config) => { const token = getAuthToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); export default axiosInstance;
- 在Home.js中使用这个实例:
import { Button, Container } from "@mui/material"; import { useSignOut } from "react-auth-kit"; import { useNavigate } from "react-router-dom"; import axiosInstance from '../utils/axiosInstance'; function Home() { const signOut = useSignOut(); const navigate = useNavigate(); const logout = () => { signOut(); navigate("login"); }; const getHelloWord = async () => { try { const response = await axiosInstance.get("hello/"); console.log("Response: ", response); } catch (error) { console.log("ERROR:", error); } }; return ( <Container> <h2>HELLO WORLD</h2> <Button onClick={getHelloWord}>HELLO WORD</Button> <Button onClick={logout}>Logout</Button> </Container> ); } export default Home;
方案2:修改Django配置,支持从Cookie读取令牌(可选)
如果不想在前端处理请求头,可以自定义Django的JWT认证类,让它同时支持从请求头和Cookie读取令牌。
- 自定义认证类:
# app/authentication.py from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework import exceptions class CookieJWTAuthentication(JWTAuthentication): def authenticate(self, request): # 优先从Authorization头读取令牌 header = self.get_header(request) if header is not None: return super().authenticate(request) # 头中没有令牌时,从Cookie读取(Cookie名要和react-auth-kit存储的一致) token = request.COOKIES.get('_auth') if not token: return None try: validated_token = self.get_validated_token(token) return self.get_user(validated_token), validated_token except exceptions.AuthenticationFailed: return None
- 修改settings.py中的认证配置:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'app.authentication.CookieJWTAuthentication', # 替换为自定义类 'rest_framework_simplejwt.authentication.JWTAuthentication', ], }
- 确保react-auth-kit把令牌存在指定Cookie中:
在login.js的signIn方法中添加存储配置:
signIn({ token: response.data.access, expiresIn: 600, tokenType: "Bearer", authState: { username: values.username }, storageType: "cookie", cookieName: "_auth", # 和Django中读取的Cookie名一致 cookieDomain: window.location.hostname, cookieSecure: false, # 开发环境设为false,生产环境改为true });
总结
- 手动添加Authorization头是标准且正确的实现方式,推荐用全局Axios拦截器简化代码
- 从Cookie读取令牌是可选方案,适合不想在前端处理请求头的场景,但需要修改Django的认证逻辑
内容的提问来源于stack exchange,提问作者Martin Bouhier
相关产品推荐
相关产品推荐

