"Forbidden (CSRF cookie not set.)" 在 Django 开发环境中使用没有有效证书的 https

"Forbidden (CSRF cookie not set.)" in Django dev env using https without valid certificate

很确定这就是我在开发环境中遇到 Forbidden (CSRF cookie not set.) 错误的原因:

所以我的问题是:

我已确认执行以下操作无效:

ALLOWED_HOSTS = [

    '*'
]

此外,GET 请求有效,但 POST 无效。

#views.py
from django.shortcuts import render
from django.views import View
from django.http import HttpResponse

# Create your views here.
class TestView(View):

    def post(self, request):
        return HttpResponse('Hello World')
// Authentication.js

  const testApiAuthentication = async () => {
    let accessToken = await authProvider.getAccessToken();
    setAccessToken(accessToken.accessToken);
    if (accessToken) {
      setAuthenticatingToken(true);
      axios({
        method: 'post',
        url: '/api/users/',
        headers: {
           Authorization: 'Bearer ' + accessToken,
        },
      })
        .then((response) => {
          console.log(response);
        })
        .catch((error) => {
          console.log(error);
        });
    }
  };

编辑:

一个选项(但不是一个好选项)是免除 CSRF。

from django.views import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt

# Create your views here.
@method_decorator(csrf_exempt, name='dispatch')
class TestView(View):

    def post(self, request):
        return HttpResponse('Hello World')

同样,这样做不是一个好主意...

我确实设法为我的开发环境实施了一个证书,认为它可以解决问题,但它仍然存在。

建议?

原来问题是我需要使用 django-rest-framework,因为根据我的阅读,它内置了 CSRF 豁免。我想我会使用 Django 视图来快速测试它,但是原来是导致问题的原因。 需要使用DRF。

以下内容仅供参考,以防在 class-based 视图上有 @csrf_exempt 或在本地开发 k8s 集群中实施 TLS 的任何人有所帮助。

正如我所指出的,一种选择(而不是一个好的选择)是将其从 CSRF 中豁免。

from django.views import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt

# Create your views here.
@method_decorator(csrf_exempt, name='dispatch')
class TestView(View):

    def post(self, request):
        return HttpResponse('Hello World')

对我有用的 TLS 实现如下(主要基于 this blog article):

  1. 确定您要为您的应用提供的 DNS 名称并将其添加到 /etc/hosts 并将其映射到 minikube ip(是的,不幸的是,如果您执行 minikube delete).在我的例子中是:
192.168.64.7      companyapp.local

然后刷新您的本地 DNS。在 macOS 上是:

sudo killall -HUP mDNSResponder
  1. 更新您的开发 ingress-nginx.yaml 以使用 hosts。我最初并没有在我的开发入口中这样做。我最终得到:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: 'nginx'
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^(/admin)$ / permanent;
  name: ingress-service-dev
  namespace: default
spec:
  tls:
    - hosts:
        - companyapp.local
      secretName: tls-companyapp-dev
  rules:
    - host: companyapp.local
      http:
        paths:
          - path: /admin/
            backend:
              serviceName: admin-cluster-ip-service-dev
              servicePort: 4000
          - path: /api/
            backend:
              serviceName: api-cluster-ip-service-dev
              servicePort: 5000
          - path: /
            backend:
              serviceName: client-cluster-ip-service-dev
              servicePort: 3000
  1. 安装 mkcert 并生成证书(请参阅 link 以获取您的 OS):
brew install mkcert
brew install nss # if you use Firefox

mkcert -install

mkcert companyapp.local
  1. 安装cert-manager:
# Kubernetes 1.16+
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.4/cert-manager.yaml

# Kubernetes <1.16
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.4/cert-manager-legacy.yaml

等待 Pods 准备好检查:

kubectl get pods --namespace cert-manager
  1. 使用来自 mkcert:
  2. 的证书创建一个 k8s 秘密条目
kubectl create secret tls tls-companyapp-dev --key=companyapp.local-key.pem --cert=companyapp.local.pem

执行此操作后您应该能够删除 .pem 个文件。

  1. 制作 IssuerCertificate 清单并应用它们:
# issuer.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-dev-issuer
  namespace: cert-manager
spec:
  ca:
    secretName: tls-companyapp-dev
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: letsencrypt-dev-certificate
  namespace: cert-manager
spec:
  secretName: tls-companyapp-dev
  dnsNames:
    - companyapp.local
  issuerRef:
    name: letsencrypt-dev-issuer
    kind: Issuer

并将它们应用于:

kubectl apply -f <manifest_location>/issuer.yaml

尝试了几次,但最终我能够导航到 companyapp.local 并且它有一个有效的证书。不必再处理“不安全”消息。

说了这么多……还是没有解决我的问题。