python - Django - 在处理端点请求之前验证 AWS Cognito token 是否有效

标签 python django amazon-web-services amazon-cognito

所以我有下面的代码用于检查 AWS Cognito token 。我显然不想将这 6 行代码添加到每个端点。另外,我不知道这是否是验证我正在做的所有事情的正确方法,即期望 token 的格式为“”,解析它并仅解码 JWT token 部分。如何对每个请求附带的 AWS amplify token 进行身份验证,以确保用户正确登录。我想将此身份验证添加到 APIView 端点和 DRF api_view 修饰端点。

View .py

import django.db.utils
from rest_framework import authentication, permissions, status
from rest_framework.views import APIView
from .serializers import *
from .models import *
from rest_framework.response import Response
from django.http import JsonResponse
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from .core.api import jwt
from django.core.exceptions import ObjectDoesNotExist
class LoginView(APIView):
    def post(self, request):
        # 'Bearer z324weroko2iorjqoi=+3r3+3ij.2o2ij4='
        token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
        print(token)
    
        # TODO this should be separated out to a login module
        try:
            res = jwt.decode_cognito_jwt(token)
            return Response(status=status.Http_200_OK)
        except:
            return Response("Invalid JWT", status=status.HTTP_401_UNAUTHORIZED)

@api_view(['GET'])
@swagger_auto_schema(
    operation_description="Get Goals joined by User"
)
def get_goals_by_user(request, user_id):
    try:
        # Get goal ids of user with id
        goals_query = JoinGoal.objects.filter(
            joiner_id=user_id).values_list('goal_id', flat=True)
        goals_list = list(goals_query)
        # Get Goals using list of goals PK with descriptions and uuid
        data = list(Goal.objects.filter(
            pk__in=goals_list).values('description', 'uuid'))
        response_data = dict(goals=data)
        return JsonResponse(response_data, status=status.HTTP_200_OK)
    except JoinGoal.DoesNotExist:
        return Response(dict(error=does_not_exist_msg(JoinGoal.__name__, 'joiner_id', user_id)), status=status.HTTP_400_BAD_REQUEST)

最佳答案

如果使用djangorestframework,@bdbd 的答案将是您的最佳选择。否则,您可能需要探索以下选项:

  1. 实现您自己的装饰器来执行身份验证。这与 @login_required 具有相同的想法装饰器或 @user_passes_test装饰师。当为基于类的 View 编写这样的装饰器时,您可能对 django.utils.decorators.method_decorator 感兴趣。 .
from functools import partial, wraps

from django.utils.decorators import method_decorator


def cognito_authenticator(view_func=None):
    if view_func is None:
        return partial(cognito_authenticator)

    @wraps(view_func)
    def wrapped_view(request, *args, **kwargs):
        # Check the cognito token from the request.
        token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]

        try:
            res = jwt.decode_cognito_jwt(token)
            # Authenticate res if valid. Raise exception if not.
        except Exception:
            # Fail if invalid
            return HttpResponseForbidden("You are forbidden here!")
        else:
            # Proceed with the view if valid
            return view_func(request, *args, **kwargs)

    return wrapped_view


# We can decorate it here before the class definition but can also be done before the class method itself. See https://docs.djangoproject.com/en/3.2/topics/class-based-views/intro/#decorating-the-class
@method_decorator(
    name="post",
    decorator=[
        cognito_authenticator,
    ],
)
class SomeView(View):
    @method_decorator(cognito_authenticator)  # As explained above, this is another way of putting the decorator
    def get(self, request):
        return HttpResponse("Allowed entry!")

    def post(self, request):
        return HttpResponse("Allowed entry!")


# Or if using function-based views
@api_view(['POST'])
@cognito_authenticator
def some_view(request):
    return HttpResponse(f"Allowed entry!")
  • 写一个custom middleware 。请注意 order很重要。与默认 AuthenticationMiddleware 相同的想法它填充 request.user 字段。在您的情况下,实现 __call__ 方法,您将在其中检查 Cognito token 。当 token 无效时,不要通过返回例如HttpResponseForbiddenreference 所示.
  • class CognitoAuthenticatorMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
    
            try:
                res = jwt.decode_cognito_jwt(token)
                # Authenticate res if valid. Raise exception if not.
            except Exception:
                # Fail if invalid
                return HttpResponseForbidden("You are forbidden here!")
    
            # Proceed if valid
            response = self.get_response(request)
    
            return response
    
    MIDDLEWARE = [
        ...
        'path.to.CognitoAuthenticatorMiddleware',
        ...
    ]
    

    更新

    这是使用选项 1 运行的示例。为简单起见,settings.py 只是默认设置。

    views.py

    from functools import partial, wraps
    
    from django.http import HttpResponse, HttpResponseForbidden
    from django.utils.decorators import method_decorator
    from django.views import View  # If using django views
    from rest_framework.views import APIView  # If using djangorestframework views
    
    
    def cognito_authenticator(view_func=None):
        if view_func is None:
            return partial(cognito_authenticator)
    
        @wraps(view_func)
        def wrapped_view(request, *args, **kwargs):
            # To simplify the authentication, we would check if there is a query parameter "name=me". If none, it is forbidden.
            if request.GET.get('name') == "me":
                return view_func(request, *args, **kwargs)
            return HttpResponseForbidden("You are forbidden here!")
    
        return wrapped_view
    
    
    @method_decorator(  # Try this style-1
        name="get",
        decorator=[
            cognito_authenticator,
        ],
    )
    class SomeView(View):  # If using djangorestframework view, this can also inherit from APIView or others e.g. class SomeView(APIView):
        @method_decorator(cognito_authenticator)  # Or try this style-2
        def get(self, request):
            return HttpResponse(f"Allowed entry!")
    

    urls.py

    from django.urls import path
    
    from my_app import views
    
    urlpatterns = [
        path("some-view/", views.SomeView.as_view()),
    ]
    

    示例运行:

    $ curl http://127.0.0.1:8000/my_app/some-view/?name=notme
    You are forbidden here!
    $ curl http://127.0.0.1:8000/my_app/some-view/?name=me
    Allowed entry!
    

    关于python - Django - 在处理端点请求之前验证 AWS Cognito token 是否有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69007786/

    相关文章:

    python - 正确的 Django-ORM 中的 latin-1 编码的 UTF-8

    node.js - 访问我在 Amazon 中的 S3 存储桶

    python - Gaendb 结构化属性

    Python pandas 如何使用 lambda 函数在 1 行中转换重复代码

    python - 自定义 NotFound 异常 --- Django REST Framework

    python - 为什么结构化菜单无法在 html 模板中呈现?

    javascript - JS 中 Promise 和回调的问题

    amazon-web-services - 我可以在 Amazon Cloudwatch 中设置报告的时区吗?

    python - 覆盖 Python 的 cmd 模块中未记录的帮助区域

    python - 如何正确设置Airflow调度程序