我在 React 应用程序中发帖时遇到以下错误
Forbidden (CSRF cookie not set.)
我看过很多类似的帖子,但它们似乎并没有解决我的问题。我相信我的问题出在我的 React 应用程序上,而不是我的 Django 端,因为我在 GET 请求上确实收到了 200 响应,但是在发布时,我的 React 应用程序无法控制我的 CSRF 令牌。
已注意到,但无法解释:当我在我的 chrome 检查器中检查应用程序时,我没有看到我的 CSRF 设置
http://localhost:3000/
姜戈
System check identified 1 issue (0 silenced).
April 01, 2023 - 22:02:51
Django version 4.1.7, using settings 'core.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
[01/Apr/2023 22:05:05] "GET /accounts/csrf-cookie HTTP/1.1" 200 29
Forbidden (CSRF cookie not set.): /accounts/register/
[01/Apr/2023 22:05:14] "POST /accounts/register/ HTTP/1.1" 403 2870
反应
Note that the development build is not optimized.
To create a production build, use npm run build.
assets by status 1.33 MiB [cached] 17 assets
assets by status 16.9 MiB [emitted]
assets by chunk 16.9 MiB (name: main)
asset static/js/bundle.js 16.9 MiB [emitted] (name: main) 1 related asset
asset main.41b1aa5256b383579099.hot-update.js 4.01 KiB [emitted] [immutable] [hmr] (name: main) 1 related asset
assets by path *.json 2.03 KiB
asset asset-manifest.json 2 KiB [emitted]
asset main.41b1aa5256b383579099.hot-update.json 28 bytes [emitted] [immutable] [hmr]
asset index.html 680 bytes [emitted]
Entrypoint main 16.9 MiB (25.6 MiB) = static/js/bundle.js 16.9 MiB main.41b1aa5256b383579099.hot-update.js 4.01 KiB 19 auxiliary assets
cached modules 14.5 MiB (javascript) 1.33 MiB (asset) [cached] 862 modules
runtime modules 29.7 KiB 16 modules
./src/components/csrfToken/csrfToken.js 2.44 KiB [built] [code generated]
webpack 5.76.3 compiled successfully in 1803 ms
settings.py
INSTALLED_APPS = [
...
'corsheaders',
'rest_framework',
...
]
-------------------
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
]
CSRF_TRUSTED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
]
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_NAME = "csrftoken"
-------------------
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, '../build')],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
-------------------
MIDDLEWARE = [
...
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
...
]
-------------------
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
]
}
views.py
@method_decorator(csrf_protect, name='dispatch')
class SignupView(APIView):
permission_classes = (permissions.AllowAny, )
def post(self, request, format=None):
data = self.request.data
username = data['username']
password = data['password']
re_password = data['re_password']
try:
if password == re_password:
if User.objects.filter(username=username).exists():
return Response({ 'error': 'Username already exists' })
else:
if len(password) < 6:
return Response({ 'error': 'Password must be at least 6 characters' })
else:
user = User.objects.create_user(username=username, password=password)
user = User.objects.get(id=user.id)
user_profile = StudentProfile.objects.create(user=user, first_name='', last_name='', phone='', city='')
return Response({ 'success': 'User created successfully' })
else:
return Response({ 'error': 'Passwords do not match' })
except:
return Response({ 'error': 'Something went wrong when registering account' })
@method_decorator(ensure_csrf_cookie, name='dispatch')
class GetCSRFToken(APIView):
permissions_classes = {permissions.AllowAny, }
def get(self, request, format=None):
return Response({'success': 'CSRF Cookie set'})
csrfToken.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const CSRFToken = () => {
const [csrftoken, setcsrftoken] = useState('');
const getCookie = (name) => {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
let cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
let csrfToken = getCookie('csrftoken');
useEffect(() => {
const fetchData = async () => {
try {
await axios.get(`${process.env.REACT_APP_API_URL}/accounts/csrf-cookie`);
console.log('success');
} catch (err) {
}
};
fetchData();
setcsrftoken(getCookie('csrfToken'));
}, []);
return (
<input type='hidden' name='csrfmiddlewaretoken' value={csrfToken} />
);
};
export default CSRFToken;
src/actions/auth.js
import axios from "axios";
import Cookies from "js-cookie";
import { REGISTER_SUCCESS, REGISTER_FAIL } from "./types";
export const register = (username, password, re_password) => async dispatch => {
const config = {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRFToken': Cookies.get('csrftoken')
}
};
console.log(config);
const body = JSON.stringify({ username, password, re_password });
try {
const res = await axios.post(`${process.env.REACT_APP_API_URL}/accounts/register`, body, config);
if (res.data.error) {
dispatch({
type: REGISTER_FAIL
});
} else {
dispatch({
type: REGISTER_SUCCESS
});
}
} catch (err) {
dispatch({
type: REGISTER_FAIL
});
}
};
src/reducers/auth.js
import { REGISTER_SUCCESS, REGISTER_FAIL } from "actions/types";
const initialState = {
isAuthenticated: null,
username: "",
first_name: "",
last_name: "",
phone: "",
zip_code: "",
};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case REGISTER_SUCCESS:
return {
...state,
isAuthenticated: false
};
case REGISTER_FAIL:
return state;
default:
return state;
}
}
src/views/auth/signUp/index.js
import React, { useState } from "react";
import { Redirect } from "react-router-dom";
import {
Button,
Flex,
FormControl,
FormLabel,
Input,
InputGroup,
Text,
} from "@chakra-ui/react";
import DefaultAuth from "layouts/auth/Default";
import CSRFToken from "components/csrfToken/csrfToken";
import illustration from "assets/img/auth/auth.png";
import { register } from "actions/auth";
import { connect } from "react-redux";
function Register({ register }) {
const [formData, setFormData] = useState({
username: "",
password: "",
re_password: "",
});
const [userCreated, setUserCreated] = useState(false);
const [showPassword, setShowPassword] = React.useState(false);
const [showRePassword, setShowRePassword] = React.useState(false);
const { username, password, re_password } = formData;
const onChange = (e) =>
setFormData({
...formData,
[e.target.name]: e.target.value,
});
const onSubmit = (e) => {
e.preventDefault();
if (password === re_password) {
register(username, password, re_password);
setUserCreated(true);
}
};
if (userCreated) {
return <Redirect to="/" />;
}
return (
<DefaultAuth illustrationBackground={illustration} image={illustration}>
<Flex>
<Flex>
<form onSubmit={(e) => onSubmit(e)}>
<CSRFToken />
<FormControl>
<FormLabel>
Username<Text>*</Text>
</FormLabel>
<Input
onChange={(e) => onChange(e)}
defaultValue={username}
/>
<FormLabel>
Password<Text>*</Text>
</FormLabel>
<InputGroup>
<Input
variant="auth"
onChange={(e) => onChange(e)}
defaultValue={password}
/>
</InputGroup>
<FormLabel>
Repeat Password<Text>*</Text>
</FormLabel>
<InputGroup>
<Input
variant="auth"
onChange={(e) => onChange(e)}
defaultValue={re_password}
/>
</InputGroup>
<Button type="submit">
Create User
</Button>
</FormControl>
</form>
</Flex>
</Flex>
</DefaultAuth>
);
}
export default connect(null, { register })(Register);