Vue Django应用程序 Forbidden (CSRF cookie not set.): 403 Forbidden

huangapple go评论71阅读模式
英文:

Vue Django app Forbidden (CSRF cookie not set.): 403 Forbidden

问题

I suppose this would actually be a django question as I think there's something wrong with the backend. I have my Vue code in frontend/ (127.0.0.1:8080) and django code in backend/ (127.0.0.1:8000). I've followed the django docs on CORS and CSRF Tokens but I get Forbidden (CSRF cookie not set.): when trying to make a post request to the django server. I'm trying to reset password via email.

backend/settings.py:

...
CORS_ALLOW_ALL_ORIGINS = True # If this is used then `CORS_ALLOWED_ORIGINS` will not have any effect
CORS_ALLOW_CREDENTIALS = True

CSRF_TRUSTED_ORIGINS = [
    'http://127.0.0.1:8080',
]

CORS_ALLOWED_ORIGINS = [
    'http://127.0.0.1:8080',
]

# SESSION_COOKIE_SAMESITE = 'None'
# CORS_ALLOW_CREDENTIALS = True

EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_DIR = BASE_DIR / 'emails'
...

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'rest_framework',
    'rest_framework.authtoken',
    'corsheaders',
    'djoser',

    'product',
    'order',
    'email_app'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
...

backend/urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('djoser.urls')),
    path('api/v1/', include('djoser.urls.authtoken')),
    path('api/v1/', include('product.urls')),
    path('api/v1/', include('order.urls')),
    path('api/v1/', include('email_app.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

backend/email_app/urls.py:

from django.urls import path

from email_app import views

urlpatterns = [
    path('reset_password/', views.reset_password),
]

backend/email_app/views.py:

from rest_framework.decorators import api_view
from django.core.mail import send_mail
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from django.template.loader import render_to_string
from rest_framework.response import Response
from rest_framework import status
from django.middleware.csrf import get_token

from django.views.decorators.csrf import csrf_protect
from rest_framework import status
from rest_framework.decorators import api_view

from .serializers import ResetPasswordEmail

@api_view(['POST', 'GET'])
@csrf_protect
def reset_password(request):
    if request.method == "POST":
        serializer = ResetPasswordEmail(data=request.data)
        if serializer.is_valid():

            # get the email from Front-End
            email = serializer.validated_data['email']

            # find the user that has that email
            user = User.objects.get(email=email)

            # get the token of that user
            token = Token.objects.get(user=user)

            if user:

                # pass the context of things above to send them in an email
                context = {
                    'email': email,
                    'username': user,
                    'token': token
                }

                send_mail(
                    'd-commerce Password Reset',
                    render_to_string('emails/reset_password.txt', context),
                    'D-COMMERCE and No Reply',
                    [email],
                    fail_silently=False,
                    auth_user=None, auth_password=None, connection=None, html_message=None
                )
                serializer.save(token=token, slug=token)
                
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
...

For vue I use:

...
// using jQuery
getCookie(name) {
   var cookieValue = null;
   if (document.cookie && document.cookie !== '') {
      var cookies = document.cookie.split(';');
      for (var i = 0; i < cookies.length; i++) {
         var cookie = jQuery.trim(cookies[i]);
         // Does this cookie string begin with the name we want?
         if (cookie.substring(0, name.length + 1) === (name + '=')) {
            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
            break;
         }
      }
   }
   return cookieValue;
}
...

and using axios to send the request:

...
const csrftoken = this.getCookie('csrftoken');
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.headers.common['X-CSRFToken'] = csrftoken;
axios.post("/api/v1/reset_password/", formData)
     .then(response => {
        toast({message: 'If you have an account with us, 
               please check your email for the link!',
               type: 'is-success',
               dismissible: true,
               pauseOnHover: true,
               position: 'bottom-right',})
     })
...
英文:

I suppose this would actually be a django question as I think there's something wrong with the backend. I have my Vue code in frontend/ (127.0.0.1:8080) and django code in backend/ (127.0.0.1:8000). I've followed the django docs on CORS and CSRF Tokens but I get Forbidden (CSRF cookie not set.): when trying to make a post request to the django server. I'm trying to reset password via email.

backend/settings.py

...
CORS_ALLOW_ALL_ORIGINS = True # If this is used then `CORS_ALLOWED_ORIGINS` will not have any effect
CORS_ALLOW_CREDENTIALS = True

CSRF_TRUSTED_ORIGINS = [
    &#39;http://127.0.0.1:8080&#39;,
]

CORS_ALLOWED_ORIGINS = [
    &#39;http://127.0.0.1:8080&#39;,
]

# SESSION_COOKIE_SAMESITE = &#39;None&#39;
# CORS_ALLOW_CREDENTIALS = True

EMAIL_BACKEND = &#39;django.core.mail.backends.filebased.EmailBackend&#39;
EMAIL_FILE_DIR = BASE_DIR / &#39;emails&#39;



# Application definition

INSTALLED_APPS = [
    &#39;django.contrib.admin&#39;,
    &#39;django.contrib.auth&#39;,
    &#39;django.contrib.contenttypes&#39;,
    &#39;django.contrib.sessions&#39;,
    &#39;django.contrib.messages&#39;,
    &#39;django.contrib.staticfiles&#39;,

    &#39;rest_framework&#39;,
    &#39;rest_framework.authtoken&#39;,
    &#39;corsheaders&#39;,
    &#39;djoser&#39;,

    &#39;product&#39;,
    &#39;order&#39;,
    &#39;email_app&#39;
]



MIDDLEWARE = [
    &#39;django.middleware.security.SecurityMiddleware&#39;,
    &#39;django.contrib.sessions.middleware.SessionMiddleware&#39;,
    &#39;corsheaders.middleware.CorsMiddleware&#39;,
    &#39;django.middleware.common.CommonMiddleware&#39;,
    &#39;django.middleware.csrf.CsrfViewMiddleware&#39;,
    &#39;django.contrib.auth.middleware.AuthenticationMiddleware&#39;,
    &#39;django.contrib.messages.middleware.MessageMiddleware&#39;,
    &#39;django.middleware.clickjacking.XFrameOptionsMiddleware&#39;,
]
...

backend/urls.py:

urlpatterns = [
    path(&#39;admin/&#39;, admin.site.urls),
    path(&#39;api/v1/&#39;, include(&#39;djoser.urls&#39;)),
    path(&#39;api/v1/&#39;, include(&#39;djoser.urls.authtoken&#39;)),
    path(&#39;api/v1/&#39;, include(&#39;product.urls&#39;)),
    path(&#39;api/v1/&#39;, include(&#39;order.urls&#39;)),
    path(&#39;api/v1/&#39;, include(&#39;email_app.urls&#39;))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

backend/email_app/urls.py:

from django.urls import path

from email_app import views

urlpatterns = [
    path(&#39;reset_password/&#39;, views.reset_password),
    
]

backend/email_app/views.py:

from rest_framework.decorators import api_view
from django.core.mail import send_mail
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from django.template.loader import render_to_string
from rest_framework.response import Response
from rest_framework import status
from django.middleware.csrf import get_token

from django.views.decorators.csrf import csrf_protect
from rest_framework import status
from rest_framework.decorators import api_view

from .serializers import ResetPasswordEmail

@api_view([&#39;POST&#39;, &#39;GET&#39;])
@csrf_protect
def reset_password(request):
    if request.method == &quot;POST&quot;:
        serializer = ResetPasswordEmail(data=request.data)
        if serializer.is_valid():

            # get the email from Front-End
            email = serializer.validated_data[&#39;email&#39;]

            # find the user that has that email
            user = User.objects.get(email=email)

            # get the token of that user
            token = Token.objects.get(user=user)

            if user:

                # pass the context of things above to send them in an email
                context = {
                    &#39;email&#39;: email,
                    &#39;username&#39;: user,
                    &#39;token&#39;: token
                }

                send_mail(
                    &#39;d-commerce Password Reset&#39;,
                    render_to_string(&#39;emails/reset_password.txt&#39;, context),
                    &#39;D-COMMERCE and No Reply&#39;,
                    [email],
                    fail_silently=False,
                    auth_user=None, auth_password=None, connection=None, html_message=None
                )
                serializer.save(token=token, slug=token)
                

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
...

For vue I use:

...
// using jQuery
getCookie(name) {
   var cookieValue = null;
   if (document.cookie &amp;&amp; document.cookie !== &#39;&#39;) {
      var cookies = document.cookie.split(&#39;;&#39;);
      for (var i = 0; i &lt; cookies.length; i++) {
         var cookie = jQuery.trim(cookies[i]);
         // Does this cookie string begin with the name we want?
         if (cookie.substring(0, name.length + 1) === (name + &#39;=&#39;)) {
            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
            break;
         }
      }
   }
   return cookieValue;
}
...

and using axios to send the request:

...
const csrftoken = this.getCookie(&#39;csrftoken&#39;);
axios.defaults.headers.common[&#39;X-Requested-With&#39;] = &#39;XMLHttpRequest&#39;;
axios.defaults.headers.common[&#39;X-CSRFToken&#39;] = csrftoken;
axios.post(&quot;/api/v1/reset_password/&quot;, formData)
     .then(response =&gt; {
        toast({message: &#39;If you have an account with us, 
               please check your email for the link!&#39;,
               type: &#39;is-success&#39;,
               dismissible: true,
               pauseOnHover: true,
               position: &#39;bottom-right&#39;,})
     })
...

答案1

得分: 0

这似乎是修复的方法,我添加了 withCredentials = true 并设置了头部如下:

axios.defaults.withCredentials = true;
axios.defaults.headers.common = {
    'X-Requested-With': 'XMLHttpRequest',
    'X-CSRFToken' : this.getCookie('csrftoken')
};
英文:

Ok so this seems to be the fix, I added withCredentials = true and set the headers like this:

axios.defaults.withCredentials = true;
axios.defaults.headers.common = {
    &#39;X-Requested-With&#39;: &#39;XMLHttpRequest&#39;,
    &#39;X-CSRFToken&#39; : this.getCookie(&#39;csrftoken&#39;)
};

huangapple
  • 本文由 发表于 2023年6月25日 22:43:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76550956.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定