美文网首页
drf Web API运用(一)--TokenAuthentic

drf Web API运用(一)--TokenAuthentic

作者: BourneKing | 来源:发表于2019-02-03 23:50 被阅读17次

    前言

    最近在研究学习django rest framework中,对于运用过的一些技术点进行总结,因此设置相关技术专题。

    基础

    官网中对Authentication解释如下:

    Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The permission and throttling policies can then use those credentials to determine if the request should be permitted.

    大概意思就是说Authentication是一种把request信息和用户身份信息绑定起来的机制,也就用来验证登录用户。
    对于本篇内容主要讲解的TokenAuthentication实际上是Authentication验证方式的一种,主要是用于数据库中通过用户登录时创建一个Token存储在数据库中,每次都会从http header中取出来于用户客户端的token进行比对。TokenAuthentication主要是运用于移动端和服务端进行交互验证。

    用法

    首先先把下面的代码放进settings.py中,用于调用各个类中的验证方法,把user放进request中。request请求的请求,Token信息是放在auth字段中

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
             ....
            'rest_framework.authentication.TokenAuthentication',
        )
    }
    

    然后,在setting中加入authtoken用于创建Token表存储用户登陆时的token

    INSTALLED_APPS = (
        ...
        'rest_framework.authtoken'
    )  
    

    接着在urls.py中增加:

    from rest_framework.authtoken import views
    urlpatterns = [
          path('api-token-auth/', views.obtain_auth_token),
    ]
    

    原理

    第一步:
    在settings.py添加AUTHENTICATION类TokenAuthentication,在执行请求的时候,会调用authenticate方法。调用之前,先通过get_authorization_header把Token取出来,注意,在用户做登录请求的时候,注意Token是放在http header中,参数格式:Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b,接着split 拿到Token信息
    源码:

    class TokenAuthentication(BaseAuthentication):
        """
        Simple token based authentication.
    
        Clients should authenticate by passing the token key in the "Authorization"
        HTTP header, prepended with the string "Token ".  For example:
    
            Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
        """
    
        keyword = 'Token'
        model = None
        """
        A custom token model may be used, but must have the following properties.
    
        * key -- The string identifying the token
        * user -- The user to which the token belongs
        """
    
        def authenticate(self, request):
            auth = get_authorization_header(request).split()
    
            if not auth or auth[0].lower() != self.keyword.lower().encode():
                return None
    
            if len(auth) == 1:
                msg = _('Invalid token header. No credentials provided.')
                raise exceptions.AuthenticationFailed(msg)
            elif len(auth) > 2:
                msg = _('Invalid token header. Token string should not contain spaces.')
                raise exceptions.AuthenticationFailed(msg)
    
            try:
                token = auth[1].decode()
            except UnicodeError:
                msg = _('Invalid token header. Token string should not contain invalid characters.')
                raise exceptions.AuthenticationFailed(msg)
    
            return self.authenticate_credentials(token)
    
    

    第二步:
    拿到用户request中的Token后,再调用authenticate_credentials方法进行验证:使用get_model方法取到数据库存储的model,在model中进行查询是否包含Token对应的信息进行判断。
    源码:

        def authenticate_credentials(self, key):
            model = self.get_model()
            try:
                token = model.objects.select_related('user').get(key=key)
            except model.DoesNotExist:
                raise exceptions.AuthenticationFailed(_('Invalid token.'))
    
            if not token.user.is_active:
                raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
    
            return (token.user, token)
    
        def authenticate_header(self, request):
            return self.keyword
    
    
        def get_model(self):
            if self.model is not None:
                return self.model
            from rest_framework.authtoken.models import Token
            return Token
    
    

    第三步
    如果用户Token表中没有信息,用户在请求的过程中,会调用试图方法中的get_or_create进行创建表,并写入用户Token信息。

    urlpatterns = [
          path('api-token-auth/', views.obtain_auth_token),
    ]
    
    class ObtainAuthToken(APIView):
        throttle_classes = ()
        permission_classes = ()
        parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
        renderer_classes = (renderers.JSONRenderer,)
        serializer_class = AuthTokenSerializer
        if coreapi is not None and coreschema is not None:
            schema = ManualSchema(
                fields=[
                    coreapi.Field(
                        name="username",
                        required=True,
                        location='form',
                        schema=coreschema.String(
                            title="Username",
                            description="Valid username for authentication",
                        ),
                    ),
                    coreapi.Field(
                        name="password",
                        required=True,
                        location='form',
                        schema=coreschema.String(
                            title="Password",
                            description="Valid password for authentication",
                        ),
                    ),
                ],
                encoding="application/json",
            )
    
        def post(self, request, *args, **kwargs):
            serializer = self.serializer_class(data=request.data,
                                               context={'request': request})
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data['user']
            token, created = Token.objects.get_or_create(user=user)
            return Response({'token': token.key})
    

    总结

    缺点:Django rest framework的Token验证,在数据库中的Token表存储的Token信息缺少一个过期时间来对Token进行管理,导致别人窃取或仿造Token进行攻击,所以这种验证方式并不安全。

    相关文章

      网友评论

          本文标题:drf Web API运用(一)--TokenAuthentic

          本文链接:https://www.haomeiwen.com/subject/czwrsqtx.html