让Django,VUE,CORS和CSRF处理真实示例

时间:2019-02-22 23:01:33

标签: django vue.js django-rest-framework django-csrf django-cors-headers

我真的很困。这就是我想要做的。

  1. 保持CSRF开。 -请不要告诉我将其关闭。
  2. 我有一个由Django和Django Rest Framework运行的API应用程序
  3. 我有一个由Vue运行的前端应用程序
  4. 我已经安装了django-cors-header来管理CORS

任何地方都适用。将其投入生产后,就会开始出现CSRF错误。这就是一切的方法。

我看到了所有答案,包括从关闭CSRF到允许所有操作的所有内容。我想这样做是正确的,而不仅仅是关闭事物并打开所有内容并最终出现安全漏洞。

所以,这就是我所拥有的。

已安装: django-cors-headers django-rest-framework drf嵌套路由器 ...和其他人

我的api运行在api.websitename.com,而Vue.js应用程序运行在websitename.com。

GET请求的效果很好。 OPTION请求似乎有效。

任何冒险的请求均不起作用。

对于我的CORS,我先安装了'corsheaders.middleware.CorsMiddleware',,然后安装了MIDDLEWARE

然后我的CORS设置是:

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
    '*.websitename.com',
)

我的CSRF设置是:

CSRF_TRUSTED_ORIGINS = [
    "api.websitename.com",
]

无论我如何使用它们,最终都会遇到CSRF令牌错误。

我已经尝试过在Vue App.vue文件中执行以下操作的方法:

mounted () {
  this.getCSRFToken()
},
methods: {
  getCSRFToken () {
    return axios.get('token/').then(response => {
      axios.defaults.headers.common['x-csrftoken'] = Cookies.get('csrftoken')
    }).catch(error => {
      return Promise.reject(error.response.data)
    })
  }
}

这个想法是,一旦APP在浏览器中加载,我就会获得CSRF令牌。但是即使如此,当应用程序尝试执行除GET或OPTION以外的任何操作时,我仍会遇到CSRF令牌错误。

这是在您有古玩的情况下返回令牌的视图:

class CSRFTokenView(APIView):
    permission_classes = (permissions.AllowAny,)

    @method_decorator(ensure_csrf_cookie)
    def get(self, request):
        return HttpResponse()

我意识到我可能在这里混了很多问题,但是欢迎提出任何可能帮助我解决问题的建议。

2 个答案:

答案 0 :(得分:2)

首先要使用SessionAuthentication

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
    )
}

这将强制执行CSRF,但匿名用户除外(稍后将对此进行详细介绍)。对于浏览器前端,最简单的解决方案是将(浏览器)前端和后端都放在同一个域中-这样可以避免CORS-如上面的注释所建议。如果您还有其他客户端,则只需使用令牌(DRF令牌或JWT)-但是由于XSS攻击的危险,这些令牌对于浏览器使用是不安全的(将令牌存储在localStorage中本质上是不安全的)。

在使用axios时,CSRF设置非常简单:

import axios from 'axios'

axios.defaults.xsrfHeaderName = 'X-CSRFToken'
axios.defaults.xsrfCookieName = 'csrftoken'

因此,您应该与CSRF进行安全会话。几乎。要引用上面的链接页面:

  

警告:创建登录页面时,请始终使用Django的标准登录视图。这将确保您的登录视图受到适当的保护。

     REST框架中的 CSRF验证与标准Django的工作原理略有不同,这是因为需要支持对同一视图的基于会话和基于非会话的身份验证。这意味着只有经过身份验证的请求才需要CSRF令牌,而匿名请求可能没有CSRF令牌就可以发送。此行为不适用于应始终应用CSRF验证的登录视图。

这很棘手-您要么只使用Django服务器端视图,这会使您的SPA设计有些复杂,要么在DRF中重新创建登录名和其他身份验证视图,但需要注意的是使用@csrf_protect方法装饰器在这些“匿名”视图上实施CSRF。显然,这样的视图将对使用令牌的客户端造成破坏,因此您可能希望为此使用不同的终结点(可能重新使用相同的基类)。因此,您的浏览器登录名使用 / auth / browser / login / 和您的移动登录名 / auth / mobile / login / ,前者使用 @csrf_protect

在研究contrib auth源代码后,应仔细地重新创建登录名和其他auth视图。对于原始要求,我建议使用django-rest-authdjango-all-auth之类的预先存在的解决方案。但是django-rest-auth软件包不是为浏览器前端设计的很好,并且会强制使用令牌生成,此外,您还需要如上所述包装视图。另一方面,all-auth为JS客户端提供AJAX响应,可能是更好的选择。

答案 1 :(得分:0)

到目前为止,解决此问题的最简单方法是为同一域中的所有内容提供服务。您可以将CDN或代理直接/api呼叫到一台服务器,其余的直接到前端服务器。这样,完全不必担心CORS。

要使其正常工作,我认为您只是在AXIOS配置中缺少withCredentials = true。 Django要求在未设置withCredentials的情况下发送CSRF cookie,并且不能通过跨源请求发送cookie。

axios.interceptors.request.use(function (config) {
  config.withCredentials = true
  return config
})

Djano的SESSION_COOKIE_DOMAIN可能是另一个可能缺少的设置。您应该这样设置:

SESSION_COOKIE_DOMAIN=".mywebsite.com"

第一个点很重要,因为它告诉Django,然后告诉网络浏览器将cookie用于*.mywebsite.com,包括api.mywebsite.com

如果所有操作仍然失败,我建议在Django的CSRF中间件上设置一个断点,以查看使它工作的缺失之处。