Django 实现JWT登录认证

依赖:

Django版本:2.2.5

djangorestframework:3.12.1

djangorestframework-jwt:1.11.0

废话不说直接上代码:

主urls.py文件(ratelimt用于登录频繁限制)

from django.contrib import admin
from django.urls import path,include,re_path
from rest_framework_jwt.views import obtain_jwt_token
from ratelimit.decorators import ratelimit


urlpatterns = [
    # jwt的认证接口
    re_path('api/(?P<version>[v1|v2]+)/login/$', ratelimit(key='ip', method='POST', rate='1/6s',block=True)(obtain_jwt_token)),
]

settings配置:

#有效期限
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),    #也可以设置seconds=?
    'JWT_AUTH_HEADER_PREFIX': 'JWT',                       #JWT跟前端保持一致,比如“token”这里设置成JWT
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app.utils.jwt_reponse_revised.jwt_response_payload_handler',  # response中token的payload部分处理函数
}

JWT_RESPONSE_PAYLOAD_HANDLER为当前项目指定文件夹下的函数名

由于源码中会调取此函数作为登录接口的Response

drf源码如下:

jwt_response_payload_handler = api_settings.JWT_RESPONSE_PAYLOAD_HANDLER



class JSONWebTokenAPIView(APIView):
    """
    Base API View that various JWT interactions inherit from.
    """
    permission_classes = ()
    authentication_classes = ()

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'view': self,
        }

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.
        You may want to override this if you need to provide different
        serializations depending on the incoming request.
        (Eg. admins get full serialization, others get basic serialization)
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__)
        return self.serializer_class

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            user = serializer.object.get('user') or request.user
            token = serializer.object.get('token')
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            if api_settings.JWT_AUTH_COOKIE:
                expiration = (datetime.utcnow() +
                              api_settings.JWT_EXPIRATION_DELTA)
                response.set_cookie(api_settings.JWT_AUTH_COOKIE,
                                    token,
                                    expires=expiration,
                                    httponly=True)
            return response

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

jwt_response_payload_handler函数内容:

def jwt_response_payload_handler(token,user=None,request=None):
    """为返回的结果添加用户相关信息"""
    return {
            'result' : {
                'token':"JWT "+token,#与配置保持一致
                'username':user.loginName,
                'userid':user.userId
                }
            }

实例:

使用:

前端需将Token添加到headers中Authorization

后端验证方式:

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class SampleView(mixins.ListModelMixin):
    queryset = Sample_info.objects.all()
    serializer_class = SampleSerializers
    module_name = 'sample'
    filter_backends = (filters.DjangoFilterBackend, Order,)
    filter_class = SampleInfoFilter
    pagination_class = SamplePagination
    ordering_fields = ('receipt_date',)
    permission_classes = [IsAuthenticated, ]
    authentication_classes = [JSONWebTokenAuthentication]#<--------token验证
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

JSONWebTokenAuthentication源码:


class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    """
    Clients should authenticate by passing the token key in the "Authorization"
    HTTP header, prepended with the string specified in the setting
    `JWT_AUTH_HEADER_PREFIX`. For example:

        Authorization: JWT eyJhbGciOiAiSFMyNTYiLCAidHlwIj
    """
    www_authenticate_realm = 'api'

    def get_jwt_value(self, request):
        auth = get_authorization_header(request).split()
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()
        if not auth:
            if api_settings.JWT_AUTH_COOKIE:
                return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
            return None
        if smart_text(auth[0].lower()) != auth_header_prefix:
            return None
        if len(auth) == 1:
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)
        return auth[1]

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        return '{0} realm="{1}"'.format(api_settings.JWT_AUTH_HEADER_PREFIX, self.www_authenticate_realm)


版权声明:本文为weixin_45388588原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。