Django Rest Framework-React应用程序尽管使用CSRF令牌,POST、PUT和DELETE HT



我使用Django rest框架作为我的API后端,React作为我的前端。我想做的是保护我的后端,这样只有前端才能使";"不安全";对它的请求,如张贴、放置、删除等。我的应用程序与区块链有关,所以我使用用户的MetaMask钱包作为身份验证手段。因此,每个用户都没有用户名或密码,所以我没有能力使用JWT。相反,我的希望是将CSRF令牌传递给前端,以使前端能够制作那些";"不安全";http请求。

我在后端设置了一个端点,以便在请求时向前端返回CSRF令牌。那个代币通过得很好。当我将该令牌包括在";"不安全";然而,我的后端返回403禁止的错误。Django文档指出,403错误可能是由于没有传递正确的CSRF令牌或权限错误造成的。作为响应,我将权限设置为";AllowAny";如下面我的settings.py文件所示。这是错误地传递CSRF令牌或错误地设置权限(或两者都不正确(的问题吗?

我的理解是,当你使用Django的CSRF中间件时,它会向前端返回一个cookie。我认为可能存在的一个潜在问题是,通常情况下,当一个cookie被传递到浏览器时,它会将该cookie设置在cookie存储中。但是,我的浏览器不会将带有CSRF令牌的cookie存储在浏览器的cookie存储中。是什么阻止了我的浏览器存储cookie?

为了澄清,每当我从前端到后端发出PUT、POST或DELETE HTTP请求时,我都会得到一个";403被禁止";错误此外,由于向CSRF令牌端点发出GET请求,我的浏览器中永远不会设置包含CSRF令牌的cookie。

React组件:这里,它是对执行";postLink";函数,该函数通常是";403被禁止";错误

import React, { useState, useEffect, useRef } from "react";
import { useLocation } from 'react-router';
import axios from "axios";
import { useParams, Link } from 'react-router-dom';
import LightSpinner from "../Components/LightSpinner";
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.defaults.withCredentials = true;
function AddLinkScreen() {
const location = useLocation();
const [link, setLink] = useState("");
const [tag, setTag] = useState("")
const [project] = useState(location.state.project)
const { name } = useParams();
const [isLoading, setIsLoading] = useState(false);
const [csrfToken, setCsrfToken] = useState("");
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current === true) {
isFirstRender.current = false
return
} else {
postLink({ link: link, tag: tag, project: project.id }, csrfToken)
.then(() => { setIsLoading(false) })
.catch((err) => { console.log(err); setIsLoading(false)})
}
}, [csrfToken])
const getCsrfToken = async() => {
await axios.get("http://127.0.0.1:8000/api/csrf/", { withCredentials: true, })
.then((res) => { setCsrfToken(res.data.csrfToken) })
.catch((err) => console.log(err))
}
const postLink = async(link, csrftkn) => {
setIsLoading(true)
await axios.post('http://127.0.0.1:8000/api/link/', link, {headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRFToken': csrftkn,
}, withCredentials: true })
.catch((err) => { console.log(err); setIsLoading(false)})
}
const onSubmitHandler = async(e) => {
e.preventDefault()
getCsrfToken()
}
return (
<div>
<div>
{isLoading ? (
<div className="spinner-position"><LightSpinner/></div>
) : (
<div>
<div>
<button className="back-button" type="button"><Link className="link-button-remove-link" to={{ pathname: `/${name}/links/` }}>Back</Link></button>
<form onSubmit={onSubmitHandler}>
<div className="edit-link-container">
<label htmlFor="link">Link: </label><br></br>
<input className="edit-link-container-input" type="text" name="link" id="link" value={link} onChange={(event) => setLink(event.target.value)} autoComplete="off"/><br></br>
<label htmlFor="tag1">Tag: </label><br></br>
<select className="edit-link-container-input" name="tag" id="tag" onChange={(e) => setTag(e.target.value)}>
<option value="" defaultValue>Tag #1</option>
<option value="Website">Website</option>
<option value="Discord">Discord</option>
<option value="Twitter">Twitter</option>
<option value="OpenSea">OpenSea</option>
<option value="Youtube">Youtube</option>
<option value="Facebook">Facebook</option>
<option value="Instagram">Instagram</option>
<option value="Other">Other</option>
</select><br></br>
</div>
<button className="edit-link-button" type="submit">Submit</button>
</form>
</div>
</div>
)}
</div>
</div>
)
}
export default AddLinkScreen;

Django获取CSRF令牌端点:

from rest_framework.decorators import api_view
from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie
@api_view(['GET'])
@ensure_csrf_cookie
def csrf(request):
tkn = get_token(request)
response = Response({'csrfToken': tkn})
response.set_cookie("X-CSRFToken", tkn, samesite="lax")
return response

Django后负载端点:

@api_view(['GET','POST'])
@csrf_protect
def LinkView(request):
if request.method == 'POST':
serializer = LinkSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)

Django settings.py:

from pathlib import Path
import os
BASE_DIR = Path(__file__).resolve().parent.parent
DEBUG = True
ALLOWED_HOSTS = ['*']
STATIC_URL = '/static/'
STATIC_ROOT =  os.path.join(BASE_DIR, 'static')
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp',
'corsheaders',
'rest_framework',
]
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'backend.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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',
],
},
},
]
WSGI_APPLICATION = 'backend.wsgi.application'
CORS_ALLOW_CREDENTIALS = True
ACCESS_CONTROL_ALLOW_HEADERS = True
CORS_ORIGIN_ALLOW_ALL = True
CSRF_COOKIE_DOMAIN = ['*']
CSRF_TRUSTED_ORIGINS = ['localhost:3000', '127.0.0.1:8000', 'localhost', '127.0.0.1', 'http://localhost:3000', '127.0.0.1:8000', 'http://localhost', 'http://127.0.0.1']
CORS_EXPOSE_HEADERS = (
'Access-Control-Allow-Origin',
'set-cookie',
"Access-Control-Expose-Headers",
'X-CSRFToken'
)
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'POST',
'PUT',
)
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'X-CSRFToken',
'x-requested-with',
'Access-Control-Allow-Origin',
'withcredentials',
'csrfmiddlewaretoken'
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES':(
'rest_framework.permissions.AllowAny',
),
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

如果有更好的方法来限制对Django API的访问,我很想听听其他方法

您没有很好地描述您的错误场景。它有时会得到403还是总是得到403?如果有的话,你什么时候得了403,什么时候没有得了403?

如果这只是偶然的,也许是因为你尝试在不同的选项卡中登录。所以csrf被轮换了。此处解释

最新更新