首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Django Rest框架--尽管使用CSRF令牌、POST、PUT和DELETE HTTP请求返回403错误,但还是反应应用程序

Django Rest框架--尽管使用CSRF令牌、POST、PUT和DELETE HTTP请求返回403错误,但还是反应应用程序
EN

Stack Overflow用户
提问于 2022-10-27 01:32:31
回答 1查看 58关注 0票数 1

我使用Django rest框架作为我的API后端,并将其作为我的前端。我想要做的是保护我的后端,这样只有前端才能向它发出“不安全”的请求,如post、put、delete等。我的应用程序与区块链有关,所以我使用用户的MetaMask钱包作为身份验证手段。因此,每个用户都没有用户名或密码,因此我无法使用JWT。相反,我希望将CSRF令牌传递给前端,使前端能够发出那些“不安全”的http请求。

我在后端设置了一个端点,以便在请求时将CSRF令牌返回到前端。这个记号很好。当我将该令牌包含在“不安全”请求的标题中时(即“”),然而,我的后端返回403禁止的错误。Django文档表明403错误可能是由于没有传递正确的CSRF令牌或权限错误造成的。作为回应,我将权限设置为"AllowAny“,如下面的settings.py文件所示。这是不正确地传递CSRF令牌,或者设置权限错误(或者两者都不正确)的问题吗?

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

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

function组件:在这里,执行"postLink“函数的响应通常是"403禁止”错误。

代码语言:javascript
运行
复制
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令牌端点:

代码语言:javascript
运行
复制
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 post负载端点:

代码语言:javascript
运行
复制
@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:

代码语言:javascript
运行
复制
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的访问,我很想听听其他的方法。

EN

回答 1

Stack Overflow用户

发布于 2022-10-27 04:00:06

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

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74215826

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档