Django部署AWS EKS后POST请求报403 CSRF Token未发送问题求助
问题回顾
基于自定义Docker镜像的Django应用部署在AWS EKS集群(镜像存储于AWS ECR),GET请求正常,但POST请求返回错误:Errore 403 forbidden CSRF Token not sent。已在settings.py中添加CSRF_TRUSTED_ORIGINS配置但未解决问题,使用AWS应用负载均衡器(ALB)作为Ingress,配置如下:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: django labels: name: django annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/backend-protocol: HTTP alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}]' alb.ingress.kubernetes.io/group.name: "alta" alb.ingress.kubernetes.io/group.order: "2" alb.ingress.kubernetes.io/healthcheck-protocol: HTTP alb.ingress.kubernetes.io/healthcheck-port: traffic-port alb.ingress.kubernetes.io/healthcheck-path: /v1/app alb.ingress.kubernetes.io/healthcheck-interval-seconds: "15" alb.ingress.kubernetes.io/healthcheck-timeout-seconds: "5" alb.ingress.kubernetes.io/success-codes: "200" alb.ingress.kubernetes.io/healthy-threshold-count: "2" alb.ingress.kubernetes.io/unhealthy-threshold-count: "2" spec: ingressClassName: alb rules: - http: paths: - pathType: Prefix path: / backend: service: name: djangoapp-cluster-ip port: number: 80
排查步骤&解决方案
1. 核对CSRF_TRUSTED_ORIGINS格式正确性
Django 2.2版本中,CSRF_TRUSTED_ORIGINS必须包含完整协议+域名(带端口需一并添加)。例如你的ALB公网域名为my-alb-123456789.us-east-1.elb.amazonaws.com,配置应为:
CSRF_TRUSTED_ORIGINS = ['http://my-alb-123456789.us-east-1.elb.amazonaws.com']
若ALB启用HTTPS,需将协议改为https://,遗漏协议是常见的配置错误。
2. 配置ALB传递反向代理请求头
ALB作为反向代理,需向Django传递X-Forwarded-Proto和X-Forwarded-Host头,确保Django识别真实请求来源。在Ingress的annotations中添加以下配置:
alb.ingress.kubernetes.io/forward-headers: "X-Forwarded-Proto,X-Forwarded-Host" alb.ingress.kubernetes.io/headers: | X-Forwarded-Proto: "$scheme" X-Forwarded-Host: "$host"
同时在Django的settings.py中开启反向代理支持:
USE_X_FORWARDED_HOST = True # 若ALB使用HTTPS,添加以下配置 SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
3. 确认CSRF Token传递逻辑
- 普通表单提交:确保模板中添加
{% csrf_token %}标签,该标签会生成隐藏token字段,供Django自动校验。 - DRF接口请求:
- 使用Session认证时,请求头需携带
X-CSRFToken,值为Cookie中的csrftoken。 - 使用Token/JWT认证时,可直接在DRF配置中关闭CSRF验证(API场景无需Session级CSRF防护):
- 使用Session认证时,请求头需携带
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication', # 移除SessionAuthentication(若不需要) ] }
若需同时支持两种认证,可给无需CSRF校验的视图添加@csrf_exempt装饰器。
4. 检查Django中间件顺序
CsrfViewMiddleware必须放在SessionMiddleware之后,否则CSRF Token无法与Session关联。settings.py中的中间件顺序需满足:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', # 必须位于SessionMiddleware之后 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
5. 打印请求头排查真实来源
在Django中添加调试视图,打印请求头确认Django接收的域名和协议是否正确:
from django.http import HttpResponse def debug_request(request): print(f"HTTP_HOST: {request.META.get('HTTP_HOST')}") print(f"X_FORWARDED_HOST: {request.META.get('HTTP_X_FORWARDED_HOST')}") print(f"X_FORWARDED_PROTO: {request.META.get('HTTP_X_FORWARDED_PROTO')}") return HttpResponse("调试信息已打印至日志")
访问该视图后,查看日志字段是否与ALB的域名、协议一致,不一致则说明ALB请求头未正确传递。
内容的提问来源于stack exchange,提问作者Abel Hristodor

