我正在尝试制作基于子域等改变用户某些字段的中间件......
唯一的问题是request.user总是在中间件中作为AnonymousUser出现,但是在视图中是正确的用户。我已经离开了django在设置中使用的默认身份验证和会话中间件。
这里有一个类似的问题:Django, request.user is always Anonymous User 但是并没有过度回答整个问题,因为我没有使用不同的身份验证方法,并且在调用我自己的中间件之前djangos身份验证正在运行。
在使用DRF时,有没有办法在中间件中获取request.user?我将在这里展示一些示例代码:
class SampleMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
#This will be AnonymousUser. I need it to be the actual user making the request.
print (request.user)
def process_response(self, request, response):
return response
使用process_request:
class SampleMiddleware(object):
def process_request(self, request):
#This will be AnonymousUser. I need it to be the actual user making the request.
print (request.user)
def process_response(self, request, response):
return response
答案 0 :(得分:26)
嘿伙计我通过从请求中获取DRF令牌并将request.user加载到与该模型关联的用户来解决此问题。
我有默认的django身份验证和会话中间件,但似乎DRF在中间件解析用户之后使用它的令牌身份验证(所有请求都是CORS请求,这可能就是原因)。这是我更新的中间件类:
from re import sub
from rest_framework.authtoken.models import Token
from core.models import OrganizationRole, Organization, User
class OrganizationMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
header_token = request.META.get('HTTP_AUTHORIZATION', None)
if header_token is not None:
try:
token = sub('Token ', '', request.META.get('HTTP_AUTHORIZATION', None))
token_obj = Token.objects.get(key = token)
request.user = token_obj.user
except Token.DoesNotExist:
pass
#This is now the correct user
print (request.user)
这也可以在process_view或process_request上使用。
希望将来可以帮助某人。
答案 1 :(得分:15)
今天碰到了这个问题。
<强> TL; DR; 强>
跳过下面的代码示例
<强>解释强>
事情是DRF有自己的事物流,就在django请求life-cycle的中间。
因此,如果正常的中间件流程是:
DRF代码会覆盖默认的django视图代码,并执行their own code。
在上面的链接中,您可以看到它们使用自己的方法包装原始请求,其中一种方法是DRF身份验证。
回到你的问题,这就是在中间件中使用request.user
为时过早的原因,因为它只有在 view_middleware **执行后得到它的值。
我使用的解决方案是让我的中间件设置为LazyObject
。
这有帮助,因为我的代码(实际的DRF ApiVIew)在实际用户已由DRF's authentication设置时执行。
这个解决方案是proposed here和讨论。
如果DRF有更好的方法来扩展其功能,可能会更好,但事实上,这似乎比提供的解决方案更好(性能和可读性都明智)。
代码示例
from django.utils.functional import SimpleLazyObject
def get_actual_value(request):
if request.user is None:
return None
return request.user #here should have value, so any code using request.user will work
class MyCustomMiddleware(object):
def process_request(self, request):
request.custom_prop = SimpleLazyObject(lambda: get_actual_value(request))
答案 2 :(得分:4)
基于Daniel Dubovski上面非常优雅的解决方案,这里是Django 1.11的中间件示例:
from django.utils.functional import SimpleLazyObject
from organization.models import OrganizationMember
from django.core.exceptions import ObjectDoesNotExist
def get_active_member(request):
try:
active_member = OrganizationMember.objects.get(user=request.user)
except (ObjectDoesNotExist, TypeError):
active_member = None
return active_member
class OrganizationMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
request.active_member = SimpleLazyObject(lambda: get_active_member(request))
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
答案 3 :(得分:3)
我知道它并没有完全回答“我们可以从中间件访问”吗?&#39;问题,但我认为这是一个更优雅的解决方案VS在中间件VS中做同样的工作VS DRJ在其基础view class中做了什么。至少对于我需要的东西,在这里添加更有意义。
基本上,我只是重写方法&#39; perform_authentication()&#39;来自DRF的代码,因为我需要在请求中添加与当前用户相关的更多内容。该方法最初调用了#request; user&#39;。
class MyGenericViewset(viewsets.GenericViewSet):
def perform_authentication(self, request):
request.user
if request.user and request.user.is_authenticated():
request.my_param1 = 'whatever'
在您自己的视图中,而不是将DRF中的APIView设置为父类,只需将该类设置为父类。
答案 4 :(得分:0)
Daniel Dubovski's solution在大多数情况下可能是最好的。
惰性对象方法的问题在于是否需要依赖副作用。就我而言,无论什么情况,我都需要为每个请求做一些事情。
如果我使用特殊的值,例如request.custom_prop
,则必须针对每次发生的副作用进行评估。我注意到了other people are setting request.user
,但对我来说不起作用,因为某些中间件或身份验证类会覆盖此属性。
如果DRF支持自己的中间件怎么办?我可以在哪里插入?在我的情况下,最简单的方法(我不需要访问request
对象,只需要经过身份验证的用户)似乎是挂接到身份验证类本身上的:
from rest_framework.authentication import TokenAuthentication
class TokenAuthenticationWithSideffects(TokenAuthentication):
def authenticate(self, request):
user_auth_tuple = super().authenticate(request)
if user_auth_tuple is None:
return
(user, token) = user_auth_tuple
# Do stuff with the user here!
return (user, token)
然后我可以在设置中替换此行:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
#"rest_framework.authentication.TokenAuthentication",
"my_project.authentication.TokenAuthenticationWithSideffects",
),
# ...
}
我不会推广此解决方案,但也许会帮助其他人。
优点:
缺点:
答案 5 :(得分:0)
我对那里的解决方案不满意。这是一个使用某些DRF内部结构的解决方案,即使视图具有特定的权限类,也要确保在中间件中应用了正确的身份验证。它使用中间件钩子process_view
,该钩子使我们可以访问将要命中的视图:
class CustomTenantMiddleware():
def process_view(self, request, view_func, view_args, view_kwargs):
# DRF saves the class of the view function as the .cls property
view_class = view_func.cls
try:
# We need to instantiate the class
view = view_class()
# And give it an action_map. It's not relevant for us, but otherwise it errors.
view.action_map = {}
# Here's our fully formed and authenticated (or not, depending on credentials) request
request = view.initialize_request(request)
except (AttributeError, TypeError):
# Can't initialize the request from this view. Fallback to using default permission classes
request = APIView().initialize_request(request)
# Here the request is fully formed, with the correct permissions depending on the view.
请注意,这不能避免必须进行两次身份验证。此后,DRF仍将很高兴进行身份验证。
答案 6 :(得分:0)
我遇到了同样的问题,因此决定更改设计。我只是使用猴子补丁rest_framework.views.APIView
而不是使用中间件。
就我而言,我需要修补check_permissions
,但是您可以修补所有适合您的问题的修补程序。看看the source code。
settings.py
INSTALLED_APPS = [
..
'myapp',
]
myapp / patching.py
import sys
from rest_framework.views import APIView as OriginalAPIView
class PatchedAPIView(OriginalAPIView):
def check_permissions(self, request):
print(f"We should do something with user {request.user}"
return OriginalAPIView.check_permissions(self, request)
# We replace the Django REST view with our patched one
sys.modules['rest_framework'].views.APIView = PatchedAPIView
myapp / __ init __。py
from .patching import *