添加CORS_TRUSTED_ORIGINS后Django仍出现CSRF错误求助
密码修改PUT请求出现403 Forbidden错误,其他PUT/POST请求正常
问题描述
实现密码修改功能时,React前端向Django后端发送PUT请求触发403错误,Django控制台提示“Origin checking failed - http://localhost:3000 does not match any trusted origins”,但其他PUT/POST请求无此问题。
代码详情
settings.py
REST_FRAMEWORK = { # 'DEFAULT_PERMISSION_CLASSES': [ # 'rest_framework.permissions.IsAuthenticated', # ], # 'DEFAULT_AUTHENTICATION_CLASSES': [ # 'rest_framework_simplejwt.authentication.JWTAuthentication', # ], 'DEFAULT_PAGINATION_CLASS': 'main.paginations.CustomPagination', 'PAGE_SIZE': 12 } CORS_ORIGIN_ALLOW_ALL = False CORS_ALLOWED_ORIGINS = [ "http://127.0.0.1:8000", "http://127.0.0.1:3000", "http://localhost:3000", ] CORS_TRUSTED_ORIGINS = [ 'http://localhost:3000', ]
views.py
class ChangePasswordView(generics.UpdateAPIView): queryset = User.objects.all() serializer_class = serializers.ChangePasswordSerializer
serializers.py
class ChangePasswordSerializer(serializers.ModelSerializer): password = serializers.CharField(write_only=True, required=True) password2 = serializers.CharField(write_only=True, required=True) old_password = serializers.CharField(write_only=True, required=True) class Meta: model = models.User fields = [ 'old_password', 'password', 'password2' ] def validate(self, attrs): if attrs['password'] != attrs['password2']: raise serializers.ValidationError({'password': 'Password fields did not match!'}) return attrs def validate_old_password(self, value): user = self.context['request'].user if not user.check_password(value): raise serializers.ValidationError({'old_password': 'Old password provided is incorrect!'}) return value def update(self, instance, validated_data): user = self.context['request'].user print(user) if user.pk != instance.pk: raise serializers.ValidationError({"authorize": "You don't have permission to change password!",}) instance.set_password(validated_data['password']) instance.save() return instance
urls.py
... path('change_password/<int:pk>/', views.ChangePasswordView.as_view(), name='auth_change_password'), ...
前端代码
const link = `${BASE_URL}${CHANGE_PASSWORD_URL}${customer_id}`; const formData = new FormData(); formData.append('old_password', oldPwdRef.current.value); formData.append('password', newPwdRef.current.value); formData.append('password2', confirmNewPwdRef.current.value); axios.put(link, formData, { headers: { 'content-type': 'application/json', }}).then((response) => { console.log(`${TAG} response is ${JSON.stringify(response)}`); }).catch(error => { console.log(`${TAG} error: ${error}`); })
错误日志
Django控制台
Forbidden (Origin checking failed - http://localhost:3000 does not match any trusted origins.): /api/change_password/2 [26/Oct/2023 06:09:11] "PUT /api/change_password/2 HTTP/1.1" 403 2569
Chrome控制台
ChangePassword: error: AxiosError: Request failed with status code 403
已尝试的方法
设置CORS_ORIGIN_ALLOW_ALL = True,问题依旧。
解决方案
1. 修正前端请求格式冲突
当前代码使用FormData却手动设置content-type: application/json,导致请求格式不匹配,后端无法正确解析数据,同时干扰CORS/CSRF验证逻辑。两种修正方式:
方式一:改用JSON格式发送数据
const link = `${BASE_URL}${CHANGE_PASSWORD_URL}${customer_id}`; const data = { old_password: oldPwdRef.current.value, password: newPwdRef.current.value, password2: confirmNewPwdRef.current.value }; axios.put(link, data, { headers: { 'content-type': 'application/json', } }).then((response) => { console.log(`${TAG} response is ${JSON.stringify(response)}`); }).catch(error => { console.log(`${TAG} error: ${error}`); })
方式二:保留FormData,移除手动content-type设置
让浏览器自动生成multipart/form-data类型的请求头:
const link = `${BASE_URL}${CHANGE_PASSWORD_URL}${customer_id}`; const formData = new FormData(); formData.append('old_password', oldPwdRef.current.value); formData.append('password', newPwdRef.current.value); formData.append('password2', confirmNewPwdRef.current.value); axios.put(link, formData).then((response) => { console.log(`${TAG} response is ${JSON.stringify(response)}`); }).catch(error => { console.log(`${TAG} error: ${error}`); })
2. 完善Django CORS配置
开启凭证支持,并补充完整的可信源地址:
# settings.py CORS_ALLOW_CREDENTIALS = True CORS_TRUSTED_ORIGINS = [ 'http://localhost:3000', 'http://127.0.0.1:3000', ]
3. 启用视图认证与权限
当前视图未配置认证类,导致request.user为匿名用户,触发序列化器中的权限验证异常,同时影响CSRF验证。取消settings.py中相关注释,启用JWT认证和登录权限:
# settings.py REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', ], 'DEFAULT_PAGINATION_CLASS': 'main.paginations.CustomPagination', 'PAGE_SIZE': 12 }
同时前端请求需携带JWT Token:
axios.put(link, data, { headers: { 'content-type': 'application/json', 'Authorization': `Bearer ${your_jwt_token}` } })
4. 适配CSRF验证(若使用Session认证)
如果采用Session认证而非JWT,需确保前端携带CSRF Token:
// 从cookie中获取CSRF Token function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } const csrftoken = getCookie('csrftoken'); // 发送请求时携带CSRF Token axios.put(link, data, { headers: { 'X-CSRFToken': csrftoken, 'content-type': 'application/json', } })
内容的提问来源于stack exchange,提问作者user22257988
相关产品推荐
相关产品推荐

