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

发布于 2025-01-23 18:58:38 字数 2152 浏览 0 评论 0原文

我在Django中有一个自定义中间件,可以强制所有请求进行登录身份验证(少数例外,例如api/token)。

该项目允许用户通过JWT令牌进行身份验证或登录 /admin/login,所有未经验证的用户都重定向到/admin/login。用于身份验证。

我们将该项目部署在Kubernetes中,我们希望Prometheus刮擦/Metrics端点,但我们不希望它将其暴露于未经身份验证的用户。 Prometheus允许使用用户名密码进行身份验证。事实是,当请求发送到/code时,由于中间件,请求将重定向到/admin/login

因此,我相信我需要编写专门为Metrics端点设计的自定义身份验证后端,然后将其放在其他身份验证方法之前。

该请求始终首先通过中间件,因此它将始终将其重定向到/admin/login,然后将通过身份验证后端进行。

正确的方法是什么?

  • Middleware.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}")
  • Backends.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

I have a custom middleware in Django to force all the requests to go through a login authentication (with few exceptions like api/token).

This project allows users to authenticate either via a JWT token or a login in
/admin/login and all unauthenticated users are redirected to /admin/login. for authentication.

We deployed the project in Kubernetes and we want Prometheus to scrape /metrics endpoint but we don't want it to be exposed to unauthenticated users. Prometheus allows for authentication with username and password. The thing is that when a request is sent to /metrics, because of the middleware, the request is redirected to /admin/login.

So I believe I need to write a custom authentication backend specifically designed for the metrics endpoint and place it before the other authentication methods.

The request always goes through the middleware first so it will always be redirected to /admin/login and then will go through the authentication backend.

What is the right way of doing this?

  • middleware.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}")
  • backends.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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

如果没有你 2025-01-30 18:58:38

让我们尝试此方法

调整中间软件以跳过将 /指标端点重定向到 /admin /登录。

将您的loginrequiredmiddleware更新到绕过 /指标身份验证检查PROMITHEUS :(

Middleware.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}")

创建一个自定义身份验证后端,用于 /度量标准,该验证可根据用户名&密码,如果有效,将返回已验证的用户。

创建另一个称为backend.py的文件(您可以随心所欲地将其命名)

backends.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

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

LOGIN_URL = '/admin/login/'

lets try this approach

Adjust the middleware to skip redirecting the /metrics endpoint to /admin/login.

Update your LoginRequiredMiddleware to bypass /metrics authentication checks for Promitheus:

(middleware.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}")

Create a custom authentication backend for /metrics that checks against a username & password, and returns an authenticated user if valid.

create another file called backend.py(you can name it whatever you like)

(backends.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

In settings.py, make sure this custom backend is included as an authentication method so it can be recognised:

(settings.py)

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

LOGIN_URL = '/admin/login/'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文