Django REST Framework之认证组件

什么是认证

认证即须要知道是谁在访问服务器,须要有一个合法身份。认证的方式能够有不少种,例如session+cookie、token等,这里以token为例。若是请求中没有token,咱们认为这是未登陆状态,有些接口要求必须登陆才能访问,若是未登陆,咱们能够一些处理(好比重定向登陆页、返回错误信息等)。前端

代码结构

urls.pypython

from django.conf.urls import url, include
from DemoApp.views import TestView
  
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

功能代码:utils/auth.py数据库

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed,ValidationError
from DemoApp.models import User

class MyAuth(BaseAuthentication):
    def authenticate(self, request):
        "认证逻辑"
        # 作认证,判断是否登录。首先拿到token,在数据库里去判断
        token = request.query_params.get("token","")
        if not token:
            raise AuthenticationFailed("没有token")
        try:
            user = User.objects.filter(token=token).first()
            if not user:
                pass
                raise AuthenticationFailed("无效的token")
        except Exception as e:
            msg = "无效的token" if hasattr(e, "messages") else "非法的token"
            raise AuthenticationFailed(msg)

        return (user, token) # 返回值是一个元素,分别对应视图中request的user,auth

views.pydjango

class TestView(APIView):
    authentication_classes = [MyAuth, ]  # 局部认证

    def get(self, request, *args, **kwargs):
        "经过认证后进入views类进行业务处理"
        print(request.user)
        print(request.auth)
        pass

在上述代码中,实现了TestView视图类的认证功能。若是认证成功,向视图类中的request对象中返回requset.user和request.auth;若是认证失败,触发异常,不会进入视图处理,直接返回前端认证失败的结果。在实际的业务中这样并不合适,咱们能够将其替代成重定向操做。编程

全局认证

  1. 将视图类中的authentication_classes属性删除
  2. settings.py配置文件中 RESTFRAMEWORK 添加配置
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] # 默认使用的认证类,全局认证
}

若是在全局认证下,有些接口不想加上认证,能够在这个类的属性 authentication_classes = [ ] 便可。服务器

除了上述本身实现的认证类,REST Framework为咱们了四种认证类:(直接在视图类中使用便可)cookie

from rest_framework.authentication import BaseAuthentication, ......
1.BasicAuthentication       基于用户名密码
2.SessionAuthentication     基于session
3.TokenAuthentication       基于token,与示例中相似
4.RemoteUserAuthentication  

源码分析

1.为何要使用authentication_classes属性变量?

  

python 的面向对象编程中,咱们首先要执行的方法确定是dispatch方法,因此咱们的分析入口就是dispatch方法,在dispatch方法中,能够看到,经过initialize_request方法将django原生的request进行了一次封装。由initialize_request方法的实现过程能够看出,将其封装实例化成了一个Request对象。而authenticators属性就是认证属性。session

经过查看get_authenticators方法,能够知道,它的返回值是一个列表生成式,而这个列表生成式中所用的就是咱们在认证类中赋值authenticatin_classes属性变量。在查找该变量的定义位置,就看到了它是经过settings配置文件来实现赋值的,除非,在子类中将其赋值。咱们的代码就是这样作的。同时,也能够看出,咱们能够修改settings配置文件来为全局定义认证规则。源码分析

2.为何认证类中要使用authenticate方法?

回到前面说是的dispatch方法来,在作完了对django原生的request的封装和实例化后,紧接着就会开始认证(try...中,捕获异常,若是没有捕获到异常,说明认证成功,就会继续执行下面的反射过程)。认证的过程就包含在上图中的inital方法中,有图可知,是经过perform_authentication方法实现的认证。url

在perform_authentication方法中能够看到,只调用了一个request.user,而这个user必定是方法,不会是属性变量,由于若是是属性变量,那么就必定有语法错误,变量必定是要赋值的,不可能孤零零的写到那里。咱们在源码中找到它,就明白了,之因此它能这么写,就是由于有了property装饰器。在user方法中找到_authenticate方法,这就是认证的方法。

在这个方法中,一切答案都就找到了。首先看authenticators,是否是很眼熟,没错它就是前面说的,封装和实例化原生request的Request类中所定义的属性变量。在实例化时,咱们就将authentication_classes列表的值经过get_authenticators方法中的列表生成式赋值给了authenticators。再往下看,authenticator.autheneicate(self)中的authenticator是否是就是咱们本身定义的认证类,而它在源码中要作“.authenticate(self)”的操做,那天然而然,咱们定义的认证类中要实现这个方法了。

3.为何认证成功后的返回值在request.user和request.auth中?

由上述图可知,当咱们认证成功后会执行“self.user, self.auth = user_auth_tuple”代码,咱们在认证类定义的方法authenticate的返回值就保存在 user_auth_tuple中,因此咱们经过request.user 和 request.auth 就能够获取到了