graphene django端点是否同时需要X-Csrftoken和CsrfCookie?

graphene django端点是否同时需要X-Csrftoken和CsrfCookie?,django,graphql,graphene-python,vue-apollo,graphene-django,Django,Graphql,Graphene Python,Vue Apollo,Graphene Django,使用: Django 3.x[Django过滤器2.2.0,石墨烯Django 2.8.0,graphql继电器2.0.1] Vue 2.x[Vue阿波罗] 我正在用Django、GraphQL和vue Apollo测试单页vue应用程序 如果我在我的视图中使用csrf_-emption,则所有内容都在前端工作 urlpatterns = [ <...> path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=Tr

使用:

  • Django 3.x[Django过滤器2.2.0,石墨烯Django 2.8.0,graphql继电器2.0.1]
  • Vue 2.x[Vue阿波罗]

我正在用Django、GraphQL和vue Apollo测试单页vue应用程序

如果我在我的视图中使用
csrf_-emption
,则所有内容都在前端工作

urlpatterns = [
<...>
   path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
<...>
或者使用
sure\u csrf\u cookie

之后,在我的
ApolloClient
中,我获取这些值并将请求头发送给他

这是当我从Django Vue页面发送GraphQL请求时Django打印的内容

Forbidden (CSRF token missing or incorrect.): /graphql
我总是使用
graphiqlide
进行并行测试,并且这些请求仍然有效。每次我还打印查询解析器的
info.context.headers

{'Content-Length': '400', 'Content-Type': 'application/json',
'Host': 'localhost:7000', 'Connection': 'keep-alive',
'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 
'Accept': 'application/json', 'Sec-Fetch-Dest': 'empty', 'X-Csrftoken': 'dvMXuYfAXowxRGtwSVYQmpNcpGrLSR7RuUnc4IbIarjljxACtaozy3Jgp3YOkMGz',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36',
'Origin': 'http://localhost:7000',
'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Mode': 'cors',
'Referer': 'http://localhost:7000/graphql', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'en-US,en;q=0.9,de;q=0.8',
'Cookie': 'sessionid=jqjvjfvg4sjmp7nkeunebqos8c7onhiz; csrftoken=dvMXuYfAXowxRGtwSVYQmpNcpGrLSR7RuUnc4IbIarjljxACtaozy3Jgp3YOkMGz'}
我发现
GraphQLView IDE
总是将
X-Csrftoken
Cookie:..Csrftoken.
也放在请求中。如果在发送请求之前删除
GraphQLView IDE
的csrftoken cookie,我会得到

Forbidden (CSRF cookie not set.): /graphql
IDE显示一个红色的长报告

.... CSRF verification failed. Request aborted.</p>\n\n\n  
<p>You are seeing this message because this site requires a CSRF cookie when submitting forms.
This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>\n
。。。。CSRF验证失败。请求已中止。

\n\n\n 您看到此消息是因为此站点在提交表单时需要CSRF cookie。 出于安全原因,需要使用此cookie,以确保您的浏览器不会被第三方劫持。

\n
IDE的信息表示请求需要CSRF cookie。但到目前为止,所有在论坛上阅读的内容都与价值本身有关。这意味着您只需在头中以
X-Csrftoken
左右的形式发送csrf值,视图就会发挥神奇的作用


问题

因此,我的问题是:

我是否必须在我的
Apollo客户端中同时设置
X-Csrftoken
Cookie:…Csrftoken
,才能对我的django
GraphQLView
发出请求


或者也可以只发送
X-Csrftoken
,而不发送
csrfcookie
,反之亦然

我花了很长时间,停顿了一会才发现这个问题,我又试了一次,找到了解决办法

设置

  • django 3.1
  • vue 2.6
  • vue apollo 3.0.4(支持新的apollo客户端3)
  • @阿波罗/客户机3.1.3
推定

  • 我将Vue用作多个应用程序,而不是单个应用程序
  • 在Django
    STATICFILES\u DIRS
    中写入我的
    *vue.js
    文件时,Webpack DevServer将热加载。Django将从那里获取文件。很好

问题回顾

在重新审视我的问题后,我发现我有两个问题。一个是浏览器因为CORS而拒绝graphQL请求。第二个是CSRF令牌


解决方案

为了解决CORS问题,我注意到Apollo客户端的
uri
与Django-Dev服务器不同。而不是
http://127.0.0.1:7000/graphql
它被设置为
http://localhost:7000/graphql
。我还设置了
凭证
(请参见vue apollo.js)

为了修复CSRF,我做了三件事

  • 确保将带有HTML的
    {%csrf_token%}
    发送到Vue/GraphQL客户端应用程序所连接的位置。这样我们以后可以去拿
  • 安装
    js cookie
    获取cookie
  • 使用
    vue Apollo.js中的
    X-CSRFToken
    在Apollo客户端构造函数中设置头
vue apollo.js


Vue.config.js


const BundleTracker=require(“网页包包跟踪器”);
//钩住你的应用程序
常量页={
“第1页”:{
条目:'./src/page_1.js',
区块:[“区块供应商”]
},
“第2页”:{
条目:'./src/page_2.js',
区块:[“区块供应商”]
},
}
module.exports={
页数:页数,
filenameHashing:false,
productionSourceMap:false,
//路径:
//告诉Django你在哪里找到包裹。
publicPath:“/static/”,
//outputDir:
//将生成生产生成文件的目录-STATICFILES\u DIRS
outputDir:“../dev_static/vue_bundle”,
链接网页包:配置=>{
配置优化
.分割块({
缓存组:{
供应商:{
测试:/[\/]节点单元模块[\\/]/,,
名称:“区块供应商”,
块:“全部”,
优先事项:1
},
},
});
//不要创建模板,因为我们使用Django模板
Object.keys(page).forEach(page=>{
delete(`html-${page}`);
delete(`preload-${page}`);
delete(`prefetch-${page}`);
})
//创建webpack-stats.json。
//此文件将描述此构建过程生成的捆绑包。
//django网页包加载器最终使用
配置
.plugin('BundleTracker')
.use(BundleTracker,[{filename:'/webpack stats.json'}]);
//添加到使用Apollo查询标记(Apollo组件)中,请参阅vue Apollo文档
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(选项=>{
选项。传输选项={
转换:{
危险标记模板字符串:true,
},
}
返回选项
})
//这将允许我们将路径引用到静态
//Vue组件中的文件
config.resolve.alias
.set(“静态”、“静态”)
//将开发服务器配置为在非生产模式下使用,
config.devServer
.公众('http://localhost:
.... CSRF verification failed. Request aborted.</p>\n\n\n  
<p>You are seeing this message because this site requires a CSRF cookie when submitting forms.
This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>\n
import Vue from 'vue'
// import path for the new Apollo Client 3 and Vue-Apollo
import { ApolloClient, InMemoryCache } from '@apollo/client/core';
import VueApollo from 'vue-apollo'
import Cookies from 'js-cookie'

  
// Create the apollo client
const apolloClient = new ApolloClient({
  // -------------------
  // # Required Fields #
  // -------------------
  // URI - GraphQL Endpoint
  uri: 'http://127.0.0.1:7000/graphql',
  // Cache
  cache: new InMemoryCache(),

  // -------------------
  // # Optional Fields #
  // -------------------
  // DevBrowserConsole
  connectToDevTools: true,
  // Else
  credentials: 'same-origin',
  headers: {
    'X-CSRFToken': Cookies.get('csrftoken')
  }
});
  
// create Vue-Apollo Instance
const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
})
  
// Install the vue plugin
Vue.use(VueApollo)
  
export default apolloProvider
const BundleTracker = require("webpack-bundle-tracker");

// hook your apps
const pages = {
    'page_1': {
        entry: './src/page_1.js',
        chunks: ['chunk-vendors']
    },
    'page_2': {
        entry: './src/page_2.js',
        chunks: ['chunk-vendors']
    },
}

module.exports = {
    pages: pages,
    filenameHashing: false,
    productionSourceMap: false,

    // puplicPath: 
    // Tells Django where do find the bundle.
    publicPath: '/static/',

    // outputDir:
    // The directory where the production build files will be generated - STATICFILES_DIRS
    outputDir: '../dev_static/vue_bundle',
 
    
    chainWebpack: config => {

        config.optimization
        .splitChunks({
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "chunk-vendors",
                    chunks: "all",
                    priority: 1
                },
            },
        });


        // Don´t create Templates because we using Django Templates
        Object.keys(pages).forEach(page => {
            config.plugins.delete(`html-${page}`);
            config.plugins.delete(`preload-${page}`);
            config.plugins.delete(`prefetch-${page}`);
        })

        // create webpack-stats.json. 
        // This file will describe the bundles produced by this build process.
        // used eventually by django-webpack-loader
        config
            .plugin('BundleTracker')
            .use(BundleTracker, [{filename: '/webpack-stats.json'}]);


        // added to use ApolloQuery Tag (Apollo Components) see vue-apollo documentation
        config.module
        .rule('vue')
        .use('vue-loader')
            .loader('vue-loader')
            .tap(options => {
            options.transpileOptions = {
                transforms: {
                dangerousTaggedTemplateString: true,
                },
            }
            return options
            })
        
        // This will allows us to reference paths to static 
        // files within our Vue component as <img src="~__STATIC__/logo.png">
        config.resolve.alias
            .set('__STATIC__', 'static')

        // configure a development server for use in non-production modes,
        config.devServer
            .public('http://localhost:8080')
            .host('localhost')
            .port(8080)
            .hotOnly(true)
            .watchOptions({poll: 1000})
            .https(false)
            .headers({"Access-Control-Allow-Origin": ["*"]})
        
        // DO have Webpack hash chunk filename
        config.output
            .chunkFilename("[id].js")
            },

    devServer: {
        writeToDisk: true
      }
};