如何在 Django 中仅为一个端点(/metrics)编写自定义身份验证后端?

问题描述 投票:0回答:1

我在 Django 中有一个自定义中间件,可以强制所有请求都经过登录身份验证(除了少数例外,例如

api/token
)。

该项目允许用户通过 JWT 令牌或登录进行身份验证

/admin/login
,所有未经身份验证的用户将被重定向到
/admin/login.
进行身份验证。

我们在 Kubernetes 中部署了该项目,我们希望 Prometheus 抓取

/metrics
端点,但我们不希望它暴露给未经身份验证的用户。 Prometheus 允许使用
username
password
进行身份验证。问题是,当请求发送到
/metrics
时,由于中间件的原因,请求被重定向到
/admin/login

所以我相信我需要编写一个专门为

metrics
端点设计的自定义身份验证后端,并将其放在其他身份验证方法之前。

请求始终首先通过中间件,因此它始终会被重定向到

/admin/login
,然后通过身份验证后端。

这样做的正确方法是什么?

  • 中间件.py
class LoginRequiredMiddleware(MiddlewareMixin):
    def __init__(self, get_response):
        self.get_response = get_response

    def process_request(self, request):
        assert hasattr(request, 'user')

        path = request.path_info.lstrip('/')

        if path == '' or path == '/':
            return self.get_response(request)

        url_is_exempt = any(url.match(path) for url in EXEMPT_URLS)

        if request.user.is_authenticated or url_is_exempt:
            # If the user is authenticated OR the URL is in the exempt list
            # go to the requested page
            return self.get_response(request)

        else:
            # Trying to access any page as a non authenticated user
            return redirect(f"{settings.LOGIN_URL}?next=/{path}")
  • 后端.py
class MetricsAuthBackend(BaseBackend):

    def authenticate(self, request, username=None, password=None):
        if '/metrics' in request.path:
            if username == "username":
                #need to fix this to use the hash of the password
                pwd_valid = check_password(password, "password")

                if pwd_valid:
                    user = User.objects.get(username=username)
                    return user

        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None
django prometheus django-authentication django-middleware
1个回答
0
投票

具体操作方法如下

调整中间件以跳过将 /metrics 端点重定向到 /admin/login。

更新您的 LoginRequiredMiddleware 以绕过 Promitheus 的 /metrics 身份验证检查:

(中间件.py)

from django.conf import settings
from django.shortcuts import redirect
from django.utils.deprecation import MiddlewareMixin
import re

class LoginRequiredMiddleware(MiddlewareMixin):
    EXEMPT_URLS = [re.compile(settings.LOGIN_URL.lstrip('/'))]
    
    def __init__(self, get_response):
        self.get_response = get_response

    def process_request(self, request):
        path = request.path_info.lstrip('/')

        #Bypass the /metrics endpoint to use custom authentication
        if path == 'metrics':
            return None

        # Check for authentication or exempt URLs
        if request.user.is_authenticated or any(url.match(path) for url in self.EXEMPT_URLS):
            return self.get_response(request)
        
        return redirect(f"{settings.LOGIN_URL}?next=/{path}")

为 /metrics 创建一个自定义身份验证后端,用于检查用户名和密码,并在有效时返回经过身份验证的用户。

创建另一个名为 backend.py 的文件(你可以随意命名)

(后端.py)

from django.contrib.auth.backends import BaseBackend
from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import check_password

User = get_user_model()

class MetricsAuthBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        # Only apply this authentication to /metrics endpoint
        if request.path == '/metrics':
            metrics_username = "username"  #replace with your metrics username
            metrics_password_hash = "hashed_password"  # replace with hashed password
            
            if username == metrics_username and check_password(password, metrics_password_hash):
                try:
                    return User.objects.get(username=username)
                except User.DoesNotExist:
                    return None
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

在 settings.py 中,确保包含此自定义后端作为身份验证方法,以便可以识别它:

(设置.py)

AUTHENTICATION_BACKENDS = [
    'path.to.backends.MetricsAuthBackend',
    'django.contrib.auth.backends.ModelBackend',  # default backend
]

LOGIN_URL = '/admin/login/'
© www.soinside.com 2019 - 2024. All rights reserved.