博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Django框架(十九)--Django rest_framework-认证组件
阅读量:5355 次
发布时间:2019-06-15

本文共 13354 字,大约阅读时间需要 44 分钟。

一、什么是认证

只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件

二、利用token记录认证过的用户

1、什么是token

token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。session的存储是需要空间的,session的传输一般都是通过cookie来传输,或url重写的方式。

token在服务器时可以不用存储用户信息的,token传递的方式也不限于cookie传递,token也可以保存起来

2、token的原理

当第一次登录认证过后,就会返回一个token到前台,前台之后发送请求,就会带上这个token字符串,

3、cookie、session、token的区别

cookie是保存在浏览器,以key:value的形式传递到服务器认证用户,用户名密码可能裸露,不安全
session是保存在服务器,产生随机字符串与用户信息对应,将随机字符串放在cookie中。服务器会保存一份,可能保存到缓存/数据库/文件
token发送请求的对象可以是浏览器,也可以是移动端。服务器不需要记录任何东西,每次都是一个无状态的请求,每次都是通过解密来验证是否合法

三、drf的认证组件

# 用户信息表class UserInfo(models.Model):    name = models.CharField(max_length=32)    pwd = models.CharField(max_length=32)# 用户tokenclass UserToken(models.Model):    token = models.CharField(max_length=64)    user = models.OneToOneField(to=UserInfo)

1、基本使用

from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import APIExceptionfrom app01 import models# 定义一个认证类class LoginAuth(BaseAuthentication):    # 重写authenticate方法    def authenticate(self, request):        # 从路由中获取token,从request对象中获取,如果token放在header中        # query_params 是Request封装的原生request.GET        token = request.query_params.get('token')        ret = models.UserToken.objects.filter(token=token).first()        if ret:            # 能查到,说明认证通过,返回空,或者返回当前的用户对象            # ret.user就是当前登录用户对象,一旦retrun了,后面的认证类都不执行了            return ret.user,ret        # 如果查不到,抛出异常        raise APIException('用户认证失败')
# view 层from rest_framework.views import APIView# 获取随机字符串——tokendef get_token(name):    md = hashlib.md5()    md.update(str(name).encode('utf-8'))    return md.hexdigest()# 用户登录class Login(APIView):    def post(self, request, *args, **kwargs):        response = {
'status': 100, 'msg': '登录成功'} name = request.data.get('name') pwd = request.data.get('pwd') ret = models.UserInfo.objects.filter(name=name, pwd=pwd).first() if ret: token = get_token(name) # 一旦用户信息校验通过,就产生一个token保存在Token表中 models.UserToken.objects.create(token=token, user=ret) response['token'] = token else: response['status'] = 101 response['msg'] = '用户名或密码错误' return JsonResponse(response, safe=False)# 查看所有图书信息 class Book(APIView): # 指定authentication_classes,会循环列表实例化,进行认证 # 该方法是局部使用认证 authentication_classes = [UserAuth.UserAuth, ] def get(self, request, *args, **kwargs): response = {
'status': 100, 'msg': '查询成功'} ret = models.Book.objects.all() ser = MySerializer.BookSerializer(instance=ret, many=True) response['data'] = ser.data return JsonResponse(response, safe=False)

2、全局使用、局部使用、局部禁用认证

(1)全局使用

  • 在settings文件中配置,配置完以后,就无需在视图类中写,已经是所有视图类都需要认证
  • 必须为REST_FRAMEWORK,key值必须为DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK={    'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',],}

(2)局部使用

在需要使用的视图类中写,只对当前视图类起认证作用,重新定义authentication_classes

class Book(APIView):    # 该方法是局部使用认证    authentication_classes = [UserAuth.UserAuth, ]    def get(self, request, *args, **kwargs):        response = {
'status': 100, 'msg': '查询成功'} ret = models.Book.objects.all() ser = MySerializer.BookSerializer(instance=ret, many=True) response['data'] = ser.data return JsonResponse(response, safe=False)

(3)局部禁用

在配置过全局认证以后,有些视图类不需要认证,可以局部禁用认证,只需将authentication_classes定义为空列表即可。例如:登录视图,不应该有认证,就可以局部禁用

# settings中REST_FRAMEWORK={    'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',],}# view的视图类中class Book(APIView):    # 该方法是局部使用认证    authentication_classes = []    def get(self, request, *args, **kwargs):        response = {
'status': 100, 'msg': '查询成功'} ret = models.Book.objects.all() ser = MySerializer.BookSerializer(instance=ret, many=True) response['data'] = ser.data return JsonResponse(response, safe=False)

3、不存数据库的token实现认证

# 登录以后,会产生一个随机字符串,返回到移动端/PC端,下一次发送请求,就在后面拼上  ?token=asdfasdgasdg|{"id":1},这样只要将后面的id通过加密获取随机字符串,与原字符串比较是否一致即可,不用再讲token存到数据库def check_token(token):    user = None    ret = True    try:        # token 拿到的是  ‘fsdfasdfasd|{\"name\": \"lqz\", \"id\": 1}’        ll = token.split('|')        md = haslib.md5()        md.update(ll[1].encode('utf-8'))        md5.update(settings.password.encode('utf-8'))        hex = md.hexdigest()        if hex == ll[0]:            user = ll[1]        else:            ret = False     except Exception as e:        ret = False     return ret,userclass LoginAuth(BaseAuthentication):    # 重写authenticate方法    def authenticate(self, request):        token = request.query_params.get('token')        ret, user_info = check_token(token)        if ret:            return user_info, None        # 如果查不到,抛异常        raise exceptions.APIException('您认证失败')
def create_token(user_id):    md5 = hashlib.md5()    md5.update(user_id.encode('utf-8'))    # 加盐加密    md5.update(settings.password.encode('utf-8'))    hex = md5.hexdigest()    # 加密完以后,直接在后面拼上id,用于认证,传过来的id加密以后是否和原token一致    token = hex + '|' + user_id    print(token)    return token# 登录# 产生随机字符串的时候就不需要存到数据库中class Login(APIView):    authentication_classes = []    def post(self, request, *args, **kwargs):        response = {
'status': 100, 'msg': '登录成功'} name = request.data.get('name') pwd = request.data.get('pwd') try: user = models.UserInfo.objects.get(name=name, pwd=pwd) user_info_json = json.dumps({
'name': user.name, 'id': user.pk}) # 生产dafgasdewf|{'name':user.name,'id':user.pk}的token token = create_token(str(user.pk)) response['token'] = token except ObjectDoesNotExist as e: response['status'] = 101 response['msg'] = '用户名或密码错误' except Exception as e: response['status'] = 102 # response['msg']='未知错误' response['msg'] = str(e) return JsonResponse(response, safe=False)

四、源码分析

as_view ----------> view -------------> dispatch -------> Request包装新的request ------> 认证、权限、频率 --------> 根据请求方式分发到不同的方法

url(r'books/',views.Book.as_view())

1、Book中没有as_view

2、APIView的as_view

class APIView(View):        @classmethod    # cls 是 Book类    def as_view(cls, **initkwargs):                    # view = super(APIView, Book).as_view(**initkwargs)        view = super(APIView, cls).as_view(**initkwargs)        view.cls = cls        view.initkwargs = initkwargs        # Note: session based authentication is explicitly CSRF validated,        # all other authentication is CSRF exempt.        return csrf_exempt(view)

3、view = super(APIView, cls).as_view(**initkwargs) ---------------------> View中的as_view

class View(object):        @classonlymethod    # cls====> Book    def as_view(cls, **initkwargs):        def view(request, *args, **kwargs):            # 实例化产生一个book对象            self = cls(**initkwargs)            if hasattr(self, 'get') and not hasattr(self, 'head'):                self.head = self.get            self.request = request            self.args = args            self.kwargs = kwargs            # 调dispatch方法            return self.dispatch(request, *args, **kwargs)        view.view_class = cls        view.view_initkwargs = initkwargs        # take name and docstring from class        update_wrapper(view, cls, updated=())        # and possible attributes set by decorators        # like csrf_exempt from dispatch        update_wrapper(view, cls.dispatch, assigned=())        return view

4、return self.dispatch(request, *args, **kwargs) ----------------> dispatch

self====> Book对象,一层层找dispatch

APIView中找到dispatch

class APIView(View):        def dispatch(self, request, *args, **kwargs):        self.args = args        self.kwargs = kwargs                # (a)初始化request,就是通过Request类来包装原生request,得到包装后的request        request = self.initialize_request(request, *args, **kwargs)        # 从现在开始request就是包装后的request        self.request = request        self.headers = self.default_response_headers  # deprecate?        try:            # (b) 认证、权限、频率            self.initial(request, *args, **kwargs)            # Get the appropriate handler method            # http_method_names表示列表['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']            if request.method.lower() in self.http_method_names:                handler = getattr(self, request.method.lower(),                                  self.http_method_not_allowed)            else:                handler = self.http_method_not_allowed            response = handler(request, *args, **kwargs)        except Exception as exc:            response = self.handle_exception(exc)        self.response = self.finalize_response(request, response, *args, **kwargs)        return self.response

(a)request = self.initialize_request(request, *args, **kwargs) 包装 request

self 是Book对象

class APIView(View):    # 默认的认证列表类    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES    def initialize_request(self, request, *args, **kwargs):        """        Returns the initial request object.        """        parser_context = self.get_parser_context(request)        # (a-b)实例化初始化产生新的request对象        return Request(            request,            parsers=self.get_parsers(),            authenticators=self.get_authenticators(),  # 认证类实例化产生的对象的列表            negotiator=self.get_content_negotiator(),            parser_context=parser_context        )
def get_authenticators(self):        """        Instantiates and returns the list of authenticators that this view can use.        """        return [auth() for auth in self.authentication_classes]
(a------1)return Request( ··· ) ----------> Request类初始化
def __init__(self, request, parsers=None, authenticators=None,                 negotiator=None, parser_context=None):        assert isinstance(request, HttpRequest), (            'The `request` argument must be an instance of '            '`django.http.HttpRequest`, not `{}.{}`.'            .format(request.__class__.__module__, request.__class__.__name__)        )        self._request = request        self.parsers = parsers or ()        self.authenticators = authenticators or ()        self.negotiator = negotiator or self._default_negotiator()        self.parser_context = parser_context        self._data = Empty        self._files = Empty        self._full_data = Empty        self._content_type = Empty        self._stream = Empty        if self.parser_context is None:            self.parser_context = {}        self.parser_context['request'] = self        self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET        force_user = getattr(request, '_force_auth_user', None)        force_token = getattr(request, '_force_auth_token', None)        if force_user is not None or force_token is not None:            forced_auth = ForcedAuthentication(force_user, force_token)            self.authenticators = (forced_auth,)

(b)self.initial(request, *args, **kwargs) -----> 认证、权限、频率

def initial(self, request, *args, **kwargs):        """        Runs anything that needs to occur prior to calling the method handler.        """        self.format_kwarg = self.get_format_suffix(**kwargs)        # Perform content negotiation and store the accepted info on the request        neg = self.perform_content_negotiation(request)        request.accepted_renderer, request.accepted_media_type = neg        # Determine the API version, if versioning is in use.        version, scheme = self.determine_version(request, *args, **kwargs)        request.version, request.versioning_scheme = version, scheme        # Ensure that the incoming request is permitted        # (b------1) 认证        self.perform_authentication(request)        # (b------2)权限        self.check_permissions(request)        # 频率        self.check_throttles(request)
(b------1) self.perform_authentication(request) -------> 认证
def perform_authentication(self, request):        """        Perform authentication on the incoming request.        Note that if you override this and simply 'pass', then authentication        will instead be performed lazily, the first time either        `request.user` or `request.auth` is accessed.        """        request.user

(b------1------1) 调用request的user方法,request是Request实例化产生的对象

class Request(object):    @property    def user(self):                # 这里的self是request        if not hasattr(self, '_user'):            with wrap_attributeerrors():                self._authenticate()   # 具体方法如下        return self._user

(b------1------1-------1) self._authenticate()

class Request(object):     def _authenticate(self):        # 这里的self是request        # self.authenticators就是在Request实例化的时候,传过来的认证类的对象的列表        # 即:[auth() for auth in self.authentication_classes]        for authenticator in self.authenticators:            try:                # 重写的就是这个authenticate()方法,                # 两个参数,第一个是对象本身(自动传);第二个是这里的self,就是request                user_auth_tuple = authenticator.authenticate(self)            except exceptions.APIException:                self._not_authenticated()                raise            if user_auth_tuple is not None:                self._authenticator = authenticator                self.user, self.auth = user_auth_tuple                return        self._not_authenticated()

 

转载于:https://www.cnblogs.com/zhangbingsheng/p/10720954.html

你可能感兴趣的文章
海量数据的处理方法
查看>>
批处理操作压缩文件
查看>>
2_sat
查看>>
Luogu P1113 杂务
查看>>
saltstack对递归依赖条件(死循环依赖)的处理
查看>>
小程序--引用公共模板方法/共同header/footer
查看>>
Android酷炫实用的开源框架(UI框架) 转
查看>>
有感而发
查看>>
LVM拓展报错及处理
查看>>
Thrift全面介绍
查看>>
七、函数(二)
查看>>
【SQL查询日志】查看数据库历史查询记录
查看>>
iOS 不规则的ImageView
查看>>
跨专业学习编程的苦逼生活 QWQ嘤嘤嘤
查看>>
进程与线程的区别
查看>>
xml文档读取-SAX
查看>>
1024 科学计数法
查看>>
MySQL中myisam和innodb的区别
查看>>
系统管理员都要知道的 30 个 Linux 系统监控工具
查看>>
tcpkill工作原理分析
查看>>