简体   繁体   English

Django Rest Framework无法识别使用Axios从React应用发送的CSRF cookie

[英]Django Rest Framework doesn't recognize CSRF cookie sent from a a React app using Axios

I have been a backend developer for many years, but now I want to learn frontend also, so I choose React as my frontend framework to start. 我已经担任后端开发人员很多年了,但是现在我也想学习前端,因此我选择React作为我的前端框架。 I have expended two days learning to send requests from a React app using Axios to a Django Rest Framework backend, and I have clashed with that csrf cookie issue. 我已经花了两天的时间来学习如何使用Axios将来自React应用的请求发送到Django Rest Framework后端,并且已经遇到了csrf cookie问题。 I have posted several questions so far, and I could, finally, been able to send a right formed request that is accepted by the backend...only to get a Forbidden (CSRF token missing or incorrect.) error. 到目前为止,我已经发布了几个问题,最后,我可以发送一个正确的格式请求,该请求被后端接受了……只是得到了一个Forbidden (CSRF token missing or incorrect.)错误。

I guess my approach to get and use the csrf token might not be the right one, so I would appreciate you to point my mistake and teach me to solve it. 我想我获取和使用csrf令牌的方法可能不正确,因此,我感谢您指出我的错误并教我解决方法。

First, I send a GET request to the backend with the only goal of getting a csrf token (which I do), and set a cookie to such token: 首先,我向后端发送GET请求,其唯一目的是获得csrf令牌 (我这样做),然后将cookie设置为此类令牌:

class App extends Component {
  render() {
    const axios = require('axios');
    axios.get('http://127.0.0.1:8000/es/api/hand_shake/')
    .then(function (response) {
      Cookies.set('csrftoken', response['data']['cookie']);
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })

    return (
      <div className="App">
        <LoginModal />
      </div>
    );
  }
}

export default App;

Second, in another module, I get the token from the cookie and use it in a POST request: 其次,在另一个模块中,我从cookie中获取令牌,并在POST请求中使用它:

handleClick() {
    const axios = require('axios');
    var csrfCookie = Cookies.get('csrftoken');
    console.log(csrfCookie)
    axios.post('http://127.0.0.1:8000/es/api-auth/login/',
      {
        next: '/',
        username: 'admin@admin.com',
        password: 'Cancun10!',
      },
      {
        headers: {
          'x-xsrf-token': csrfCookie,
        },
        withCredentials: true,
      }
    )
    .then(function (response) {
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })
  }

I have, finally, got the backend to accept the request, but I get the error Forbidden (CSRF token missing or incorrect.) . 最后,我有后端可以接受请求,但是却收到错误信息Forbidden (CSRF token missing or incorrect.)

Is it right to get the token the way I am doing it, and then to use it the way I am attempting? 以我的方式获取令牌然后以尝试的方式使用令牌是否正确?

The only solution I found was to exempt login process from csrf validation, but I had to give up Django Rest Framework default authentication functionality (if anybody knows how to do it with DRF default authentication, please share) and implement my own custom authentication view. 我发现的唯一解决方案是使登录过程免于csrf验证,但是我不得不放弃Django Rest Framework默认身份验证功能(如果有人知道如何使用DRF默认身份验证,请分享),并实现我自己的自定义身份验证视图。

I created a ViewSet and decorated it with csrf_exempt , like this: 我创建了一个ViewSet并用csrf_exempt装饰它,如下所示:

from django.views.decorators.csrf import csrf_exempt

class LoginViewSet(viewsets.ViewSet):
    permission_classes = ()

    @csrf_exempt
    def post(self, request):
        from PhotosManagerApp.email_backend import EmailBackend

        email_backend_instance = EmailBackend()
        user = email_backend_instance.authenticate(request, username=request.data['username'], password=request.data['password'])
        if user:
            try:
                usr_token = Token.objects.get(user=user)
            except:
                usr_token = None
        else:
            usr_token = None

        return Response({'token': str(usr_token)})

You also have to decorate your view in urls.py : 您还必须在urls.py装饰视图:

path('login/', csrf_exempt(views.LoginViewSet.as_view({'post': 'post'}))),

I had many troubles with Django settings.py , until I found the right configuration: 在找到正确的配置之前,我对Django settings.py遇到了很多麻烦:

from corsheaders.defaults import default_headers

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = default_headers + (
    'xsrfheadername',
    'xsrfcookiename',
    'content-type',
    'x-csrftoken',
)

CORS_ORIGIN_ALLOW_ALL = True
CSRF_COOKIE_NAME = "csrftoken"

I had many troubles until I found that I had to add default_headers to CORS_ALLOW_HEADERS . 我遇到很多麻烦,直到发现必须将default_headers添加到CORS_ALLOW_HEADERS

Now, just ensure to send your request with withCredentials = false : 现在,只需确保使用withCredentials = false发送您的请求:

handleClick() {
    const axios = require('axios');
    //axios.defaults.withCredentials = true;
    axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
    axios.defaults.xsrfCookieName = "csrftoken";
    axios.post('http://127.0.0.1:8000/es/api/login/',
      {
        username: 'admin@admin.com',
      },
    )
    .then(function (response) {
      Cookies.set('token', "Token ".concat(response.data.token), {"expires": 10})
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })
  }

This is what worked for me. 这对我有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM