昨日回顾
| |
| -有些接口需要登录后才能访问 |
| -原生django如何使用的认证: auth的user表, auth自带了认证 |
| -自己登录, 使用自定义的用户表 |
| -认证类的使用步骤: |
| -1 写一个类, 继承BaseAuthtication |
| -2 重写 authenticate 在方法中完成认证, 并且获取到当前登录用户,返回 |
| -代码从地址栏中取 |
| -request.query_params.get('token') |
| -原生django 取出前端传入cookie, 从哪取得? |
| -request.COOKIE.get('sessionid') |
| -后期如果想从请求头中取 |
| request.META.get('HTTP_TOKEN') |
| -如果认证通过,返回两个值, 第一个是当前登录用户,第二个是token,或者返回None |
| -3 局部全局使用 |
| |
| |
| |
| -写类继承SimpleRateThrottle |
| -重写get_cache_key, 返回什么, 就以什么做频率限制 |
| -类属性:scope='名字' |
| -配合配置文件中: DEFAULT_THROTTLE_RATES |
| -局部和全局使用 |
| |
| |
| |
| -内置的: 模糊匹配 |
| -第三方: 精准匹配 |
| -自定义过滤类 |
| -filter_backends |
| |
| |
| -内置排序即可 |
| |
| |
| |
| -三种分页方式: 重写三个类 |
| -但是一个接口只能有一种分页类 |
原生django的cookie+session认证底层原理
断点调试使用
| |
| |
| |
| -以debug形式运行 |
| -在左侧空白处 点击加入断点 |
| -step over 单步调试 |
| -step into 进入到函数内部运行 |
| -快速调到下一个断点 绿色箭头 |
1 认证,权限,频率源码分析
1.1权限类的执行源码
| |
| -写一个权限类,局部使用,配置在视图类的,就会执行权限类的has_permission方法,完成权限校验 |
| |
| |
| |
| -497行左右, self.initial(request, *args, **kwargs)---》执行3大认证 |
| |
| |
| def initial(self, request, *args, **kwargs): |
| |
| self.format_kwarg = self.get_format_suffix(**kwargs) |
| neg = self.perform_content_negotiation(request) |
| request.accepted_renderer, request.accepted_media_type = neg |
| version, scheme = self.determine_version(request, *args, **kwargs) |
| request.version, request.versioning_scheme = version, scheme |
| |
| |
| self.perform_authentication(request) |
| |
| self.check_permissions(request) |
| |
| self.check_throttles(request) |
| |
| |
| |
| |
| def check_permissions(self, request): |
| |
| |
| for permission in self.get_permissions(): |
| |
| |
| if not permission.has_permission(request, self): |
| |
| |
| self.permission_denied( |
| request, |
| message=getattr(permission, 'message', None), |
| code=getattr(permission, 'code', None) |
| ) |
| |
| |
| |
| def get_permissions(self): |
| |
| |
| |
| return [permission() for permission in self.permission_classes] |
| |
| |
| |
| |
| |
| -APIView---dispatch----》initial---》倒数第二行---》self.check_permissions(request) |
| 里面取出配置在视图类上的权限类,实例化得到对象,一个个执行对象的has_permission方法,如果返回False,就直接结束,不再继续往下执行,权限就认证通过 |
| |
| -如果视图类上不配做权限类:permission_classes = [CommonPermission],会使用配置文件的api_settings.DEFAULT_PERMISSION_CLASSES |
| 优先使用项目配置文件,其次使用drf内置配置文件 |
1.2认证源码分析
| |
| -497行左右, self.initial(request, *args, **kwargs)---》执行3大认证 |
| |
| |
| def initial(self, request, *args, **kwargs): |
| |
| self.format_kwarg = self.get_format_suffix(**kwargs) |
| neg = self.perform_content_negotiation(request) |
| request.accepted_renderer, request.accepted_media_type = neg |
| version, scheme = self.determine_version(request, *args, **kwargs) |
| request.version, request.versioning_scheme = version, scheme |
| |
| |
| self.perform_authentication(request) |
| |
| self.check_permissions(request) |
| |
| self.check_throttles(request) |
| |
| |
| |
| def perform_authentication(self, request): |
| request.user |
| |
| |
| |
| @property |
| def user(self): |
| if not hasattr(self, '_user'): |
| with wrap_attributeerrors(): |
| self._authenticate() |
| return self._user |
| |
| |
| |
| def _authenticate(self): |
| |
| |
| for authenticator in self.authenticators: |
| try: |
| |
| |
| 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() |
| |
| |
| |
| |
| def __init__(self, request, parsers=None, authenticators=None, |
| negotiator=None, parser_context=None): |
| ..... |
| self.authenticators = authenticators or () |
| ..... |
| |
| |
| |
| def initialize_request(self, request, *args, **kwargs): |
| return Request( |
| request, |
| parsers=self.get_parsers(), |
| authenticators=self.get_authenticators(), |
| negotiator=self.get_content_negotiator(), |
| parser_context=parser_context |
| ) |
| |
| |
| |
| |
| |
| |
| |
| 1 配置在视图类上的认证类,会在执行视图类方法之前执行,在权限认证之前执行 |
| 2 自己写的认证类,可以返回两个值或None |
| 3 后续可以从request.user 取出当前登录用户(前提是你要在认证类中返回) |
1.3频率源码分析
| |
| -497行左右, self.initial(request, *args, **kwargs)---》执行3大认证 |
| |
| |
| def initial(self, request, *args, **kwargs): |
| |
| self.format_kwarg = self.get_format_suffix(**kwargs) |
| neg = self.perform_content_negotiation(request) |
| request.accepted_renderer, request.accepted_media_type = neg |
| version, scheme = self.determine_version(request, *args, **kwargs) |
| request.version, request.versioning_scheme = version, scheme |
| |
| |
| self.perform_authentication(request) |
| |
| self.check_permissions(request) |
| |
| self.check_throttles(request) |
| |
| |
| |
| def check_throttles(self, request): |
| throttle_durations = [] |
| |
| |
| |
| for throttle in self.get_throttles(): |
| if not throttle.allow_request(request, self): |
| throttle_durations.append(throttle.wait()) |
| |
| if throttle_durations: |
| |
| |
| durations = [ |
| duration for duration in throttle_durations |
| if duration is not None |
| ] |
| |
| duration = max(durations, default=None) |
| self.throttled(request, duration) |
| |
| |
| |
| |
| -我们写的频率类:继承BaseThrottle,重写allow_request,在内部判断,如果超频了,就返回False,如果没超频率,就返回True |
1.4自定义频率类
| class SuperThrottle(BaseThrottle): |
| VISIT_RECORD = {} |
| |
| def __init__(self): |
| self.history = None |
| |
| def allow_request(self, request, view): |
| |
| |
| |
| |
| |
| |
| |
| ip = request.META.get('REMOTE_ADDR') |
| import time |
| ctime = time.time() |
| |
| if ip not in self.VISIT_RECORD: |
| self.VISIT_RECORD[ip] = [ctime, ] |
| return True |
| |
| self.history = self.VISIT_RECORD.get(ip,[]) |
| |
| while self.history and ctime - self.history[-1] > 60: |
| self.history.pop() |
| |
| |
| if len(self.history) < 3: |
| self.history.insert(0, ctime) |
| return True |
| else: |
| |
| return False |
| |
| def wait(self): |
| import time |
| ctime = time.time() |
| return 60 - (ctime - self.history[-1]) |
1.5 SimpleRateThrottle
| |
| |
| |
| def allow_request(self, request, view): |
| |
| if self.rate is None: |
| return True |
| |
| |
| self.key = self.get_cache_key(request, view) |
| if self.key is None: |
| return True |
| |
| self.history = self.cache.get(self.key, []) |
| self.now = self.timer() |
| while self.history and self.history[-1] <= self.now - self.duration: |
| self.history.pop() |
| if len(self.history) >= self.num_requests: |
| return self.throttle_failure() |
| return self.throttle_success() |
| |
| |
| |
| |
| def __init__(self): |
| if not getattr(self, 'rate', None): |
| |
| self.rate = self.get_rate() |
| |
| self.num_requests, self.duration = self.parse_rate(self.rate) |
| |
| def get_rate(self): |
| |
| if not getattr(self, 'scope', None): |
| msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % |
| self.__class__.__name__) |
| raise ImproperlyConfigured(msg) |
| |
| try: |
| |
| |
| return self.THROTTLE_RATES[self.scope] |
| except KeyError: |
| msg = "No default throttle rate set for '%s' scope" % self.scope |
| raise ImproperlyConfigured(msg) |
| |
| |
| |
| def parse_rate(self, rate): |
| |
| if rate is None: |
| return (None, None) |
| |
| |
| num, period = rate.split('/') |
| |
| num_requests = int(num) |
| duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] |
| |
| return (num_requests, duration) |
2 基于APIView编写分页
| |
| |
| class BookView(ViewSetMixin, APIView): |
| def list(self, request): |
| books = Book.objects.all() |
| |
| |
| paginator = CommonLimitoffsetpagination() |
| |
| page = paginator.paginate_queryset(books, request, self) |
| if page is not None: |
| serializer = BookSerializer(instance=page, many=True) |
| |
| |
| return Response({ |
| 'total': paginator.count, |
| 'next': paginator.get_next_link(), |
| 'previous': paginator.get_previous_link(), |
| 'results': serializer.data |
| }) |
3 异常处理
| |
| |
| -主动抛的非drf异常 |
| -程序出错了 |
| 都不会被处理 |
| 我们的目标,无论主动抛还是程序运行出错,都同意返回规定格式--》能记录日志 |
| 公司里一般返回 {code:999,'msg':'系统错误,请联系系统管理员'} |
| |
| |
| |
| |
| |
| def common_exception_handler(exc, context): |
| |
| |
| |
| |
| print('时间,登录用户id,用户ip,请求方式,请求地址,执行的视图类,错误原因') |
| res = exception_handler(exc, context) |
| if res: |
| |
| res = Response(data={'code': 888, 'msg': res.data.get('detail', '请联系系统管理员')}) |
| else: |
| |
| |
| res = Response(data={'code': 999, 'msg': '系统错误,请联系系统管理员'}) |
| |
| |
| return res |
| |
| |
| |
| REST_FRAMEWORK = { |
| 'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler', |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了