石墨烯 django 端点是不是同时需要 X-Csrftoken 和 CsrfCookie?
Posted
技术标签:
【中文标题】石墨烯 django 端点是不是同时需要 X-Csrftoken 和 CsrfCookie?【英文标题】:Does a graphene django Endpoint expects a X-Csrftoken and CsrfCookie at the same time?石墨烯 django 端点是否同时需要 X-Csrftoken 和 CsrfCookie? 【发布时间】:2020-06-17 09:29:05 【问题描述】:使用:
Django 3.x [Django-Filters 2.2.0、graphene-django 2.8.0、graphql-relay 2.0.1] Vue 2.x [Vue-Apollo]我正在使用 Django、GraphQL 和 Vue-Apollo 测试单页 vue 应用程序。
如果我在我的视图中使用csrf_exempt
,一切都在前端工作。
urlpatterns = [
<...>
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
<...>
现在我想通过 CSRF 保护我的请求。
在了解 CSRF 保护的过程中,我认为 Django GraphQLView
需要的只是在请求标头中接收 X-Csrftoken
的“值”。所以我专注于以不同的方式发送csrf
值......通过这样的单一视图
path('csrf/', views.csrf),
path("graphql", GraphQLView.as_view(graphiql=True)),
或者通过ensure_csrf_cookie
确保一个cookie
之后在我的ApolloClient
中,我获取了这些值并将其与请求 Header 一起发回。
当我从 Django-Vue 页面发送 GraphQL 请求时,这就是 Django 打印的内容。
Forbidden (CSRF token missing or incorrect.): /graphql
我总是使用graphiql IDE
进行并行测试,这些请求仍然有效。每次查询解析器的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
IDE 的信息说请求需要一个 CSRF cookie。但是到目前为止,在 Doc 的论坛中阅读的所有内容都与价值本身有关。这意味着您只需要将 Header 中的 csrf 值发送为 X-Csrftoken
左右,View 就会发挥作用。
问题
因此我的问题是:
我是否必须在我的ApolloClient
中同时设置X-Csrftoken
和Cookie:..csrftoken
才能在我的django GraphQLView
上发出请求?
或者是否也可以只发送X-Csrftoken
而没有csrf-cookie
,反之亦然?
【问题讨论】:
【参考方案1】:经过很长时间和暂停关注问题后,我再次尝试并找到了解决方案。
设置
django 3.1 vue 2.6 vue-apollo 3.0.4(支持新的 Apollo-Client 3) @apollo/client 3.1.3推定
我将 Vue 用作多应用程序而不是单个应用程序。 在 DjangoSTATICFILES_DIRS
中写入我的 *vue.js
文件时,Webpack DevServer 将热重载。 Django 将从那里获取文件。工作正常
问题回顾
重新审视我的问题后,我注意到我有 2 个问题。一个是由于 CORS,浏览器拒绝了 graphQL 请求。第二个是 CSRF Token。
解决方案
为了修复 CORS 问题,我注意到我的 Apollo 客户端 uri
与我的 Django 开发服务器不同。而不是http://127.0.0.1:7000/graphql
,它被设置为http://localhost:7000/graphql
。我还设置了credentials
(参见 vue-apollo.js)
为了修复 CSRF,我做了 3 件事
确保发送% csrf_token %
与您的 Vue/GraphQL 客户端应用程序挂钩的 HTML。以便我们稍后获取它。
安装js-cookie
获取Cookie
在 Apollo 客户端构造函数中使用 X-CSRFToken
在 vue-apollo.js
中设置标题
vue-apollo.js
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
Vue.config.js
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
;
【讨论】:
以上是关于石墨烯 django 端点是不是同时需要 X-Csrftoken 和 CsrfCookie?的主要内容,如果未能解决你的问题,请参考以下文章