我在 Django 中有一个自定义中间件,可以强制所有请求都经过登录身份验证(除了少数例外,例如
api/token
)。
该项目允许用户通过 JWT 令牌或登录进行身份验证
/admin/login
,所有未经身份验证的用户将被重定向到 /admin/login.
进行身份验证。
我们在 Kubernetes 中部署了该项目,我们希望 Prometheus 抓取
/metrics
端点,但我们不希望它暴露给未经身份验证的用户。 Prometheus 允许使用 username
和 password
进行身份验证。问题是,当请求发送到 /metrics
时,由于中间件的原因,请求被重定向到 /admin/login
。
所以我相信我需要编写一个专门为
metrics
端点设计的自定义身份验证后端,并将其放在其他身份验证方法之前。
请求始终首先通过中间件,因此它始终会被重定向到
/admin/login
,然后通过身份验证后端。
这样做的正确方法是什么?
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}")
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
具体操作方法如下
调整中间件以跳过将 /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/'