drf的token登录和原理

1.token登录实现

参考django restframework文档关于TokenAuthentication

1.1 在setting.py中加入authtoken

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]
REST_FRAMEWORK = {
    # 用于验证用户登录信息
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ]
}

1.2 迁移关于token相关数据表

  • python manage.py makemigrations
  • python manage.py migrate


    截屏2022-11-13 20.05.39.png

1.3 url.py中配置路由

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

1.4 postman测试接口

截屏2022-11-13 20.08.57.png

截屏2022-11-13 20.24.00.png

2.登录原理

通过接口获取token实现

class ObtainAuthToken(APIView):
    ....
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        # 查询authtoken_token表中的token.key
        # 如果没有则插入数据
        token, created = Token.objects.get_or_create(user=user)
        return Response({'token': token.key})
obtain_auth_token = ObtainAuthToken.as_view()

通过token进行接口验证

# rest_framework/authentication.py
class TokenAuthentication(BaseAuthentication):
    keyword = 'Token'
    model = None

    def  get_model(self):
        if self.model is not None:
            return self.model
        from rest_framework.authtoken.models import Token
        return Token

    def authenticate(self, request):
        # 获取token(Token ff000224e411d7676cc1e0da2d0a1ace6c372df1)并分割
        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为ff000224e411d7676cc1e0da2d0a1ace6c372df1
            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)

    def authenticate_credentials(self, key):
        # 获取表authtoken_token的模型
        model = self.get_model()
        try:
            # 校验用户的token是否存在或有效
            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)

3.缺点

  • token是永久的,没有过期时间,泄漏会有问题
  • 分布式的应用,需要同步用户信息到每个服务器
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容