drf第五讲——请求的跨域、频率限制、jwt

内容回顾

1.restful规范?

第一步:整体说restful规范是什么?

第二步:再详细说restful建议
	1. https代替http,保证数据传输时安全。
	2. 在url中一般要体现api标识,这样看到url就知道他是一个api。
		http://www.luffycity.com/api/....(建议,因为他不会存在跨域的问题)
		http://api.luffycity.com/....
		假设:
			前段:https://www.luffycity.com/home
			后端:https://www.luffycity.com/api/
	3. 在接口中要体现版本
		http://www.luffycity.com/api/v1....(建议,因为他不会存在跨域的问题)
		注意:版本还可以放在请求头中
			http://www.luffycity.com/api/
			accept: ...
			
	4. restful也称为面向资源编程,视网络上的一切都是资源,对资源可以进行操作,所以一般资源都用名词。
		http://www.luffycity.com/api/user/
		
	5. 如果要加入一些筛选条件,可以添加在url中	
		http://www.luffycity.com/api/user/?page=1&type=9

	6. 根据method不同做不同操作。

下面的可以不说但是得记住:

	7. 返回给用户状态码
		- 200,成功
		- 300,301永久 /302临时
		- 400,403拒绝 /404找不到
		- 500,服务端代码错误
		
		很多公司:
                def get(self,request,*args,**kwargs):
                    result = {'code':1000,'data':None,'error':None}
                    try:
                        val = int('你好')
                    except Exception as e:
                        result['code'] = 10001
                        result['error'] = '数据转换错误'

                    return Response(result)
        
	8. 返回值
		GET http://www.luffycity.com/api/user/
			[
				{'id':1,'name':'alex','age':19},
				{'id':1,'name':'alex','age':19},
			]
		POST http://www.luffycity.com/api/user/
			{'id':1,'name':'alex','age':19}
			
		GET http://www.luffycity.com/api/user/2/
			{'id':2,'name':'alex','age':19}
			
		PUT http://www.luffycity.com/api/user/2/
			{'id':2,'name':'alex','age':19}
		
		PATCH https//www.luffycity.com/api/user/2/
			{'id':2,'name':'alex','age':19}
			
		DELETE https//www.luffycity.com/api/user/2/
			空
	9. 操作异常时,要返回错误信息
	
		{
            error: "Invalid API key"
        }
	10. 对于下一个请求要返回一些接口:Hypermedia AP
		{
			'id':2,
			'name':'alex',
			'age':19,
			'depart': "http://www.luffycity.com/api/user/30/"
		}

2.drf框架

记忆:请求到来之后,先执行视图的dispatch方法。

1. 视图
2. 版本处理
3. 认证
4. 权限
5. 节流(频率限制)
6. 解析器
7. 筛选器
8. 分页
9. 序列化
10. 渲染

1.跨域

链接:https://chpl.top/2019/10/05/%E4%B9%A6/Django/%E7%AC%AC%E4%BA%8C%E5%8D%81%E8%AE%B2%E2%80%94%E2%80%94Django%E7%BB%BC%E5%90%88%E6%89%A9%E5%B1%95%E5%8D%81%E4%B9%8B%E8%B7%A8%E5%9F%9F%E5%92%8CCORS/

由于浏览器具有“同源策略”的限制。
如果在同一个域下发送ajax请求,浏览器的同源策略不会阻止。
如果在不同域下发送ajax,浏览器的同源策略会阻止。
总结
  • 域相同,永远不会存在跨域。
    • crm,非前后端分离,没有跨域。
    • 路飞学城,前后端分离,没有跨域(之前有,现在没有)。
  • 域不同时,才会存在跨域。
    • l拉勾网,前后端分离,存在跨域(设置响应头解决跨域)
解决跨域:CORS
本质在数据返回值设置响应头

from django.shortcuts import render,HttpResponse

def json(request):
    response = HttpResponse("JSONasdfasdf")
    response['Access-Control-Allow-Origin'] = "*"
    return response
    
跨域时,发送了2次请求?

在跨域时,发送的请求会分为两种:

  • 简单请求,发一次请求。

    设置响应头就可以解决
    from django.shortcuts import render,HttpResponse
    
    def json(request):
        response = HttpResponse("JSONasdfasdf")
        response['Access-Control-Allow-Origin'] = "*"
        return response
    
  • 复杂请求,发两次请求。

    • 预检
    • 请求

@csrf_exempt
def put_json(request):
    response = HttpResponse("JSON复杂请求")
    if request.method == 'OPTIONS':
        # 处理预检
        response['Access-Control-Allow-Origin'] = "*"
        response['Access-Control-Allow-Methods'] = "PUT"
        return response
    elif request.method == "PUT":
        return response
条件:
    1、请求方式:HEAD、GET、POST
    2、请求头信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 对应的值是以下三个中的任意一个
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain
 
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
总结

1.由于浏览器具有“同源策略”的限制,所以在浏览器上跨域发送 Ajax请求时,会被浏览器阻止。

2.解决跨域

  • CORS(跨站资源共享,本质是设置响应头来解决)。
    • 简单请求:发送一次请求
    • 复杂请求:发送两次请求

2.drf的访问频率限制

  • 频率限制在版本、认证、权限之后

  • 知识点

    {
    	throttle_anon_1.1.1.1:[100121340,],
    	1.1.1.2:[100121251,100120450,]
    }
    
    限制:60s能访问3次
    来访问时:
    	1.获取当前时间 100121280
    	2.100121280-60 = 100121220,小于100121220所有记录删除
    	3.判断1分钟以内已经访问多少次了? 4 
    	4.无法访问
    停一会
    来访问时:
    	1.获取当前时间 100121340
    	2.100121340-60 = 100121280,小于100121280所有记录删除
    	3.判断1分钟以内已经访问多少次了? 0
    	4.可以访问
    
源码

重写方法

from rest_framework.throttling import SimpleRateThrottle


class AnonThrottle(SimpleRateThrottle):
	scope = 'anon'  # 相当于设置了最大的访问次数和时间

	def get_cache_key(self, request, view):
		if request.user:
			return None  # 返回None表示我不限制,登录用户我不管
			# 匿名用户
		return self.get_ident(request)  # 返回一个唯一标识IP


class UserThrottle(SimpleRateThrottle):
	scope = 'user'

	def get_cache_key(self, request, view):
		# 登录用户
		if request.user:
			return request.user
		return None  # 返回NOne表示匿名用户我不管

在视图中使用

from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework.throttling import AnonRateThrottle,BaseThrottle

class ArticleView(APIView):
    throttle_classes = [AnonRateThrottle,]
    def get(self,request,*args,**kwargs):
        return Response('文章列表')
    
    	def throttled(self, request, wait):
		'''可定制方法设置中文错误'''

		# raise exceptions.Throttled(wait)
		class MyThrottle(exceptions.Throttled):
			default_detail = '请求被限制'
			extra_detail_singular = 'Expected available in {wait} second.'
			extra_detail_plural = 'Expected available in {wait} seconds.'
			default_code = '还需要再等{wait}秒'

		raise MyThrottle(wait)

class ArticleDetailView(APIView):
    def get(self,request,*args,**kwargs):
        return Response('文章列表')
dispacth
def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        # 1. 对request进行加工
        # request封装了
        """
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
        """
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            # 初始化request
            # 确定request版本,用户认证,权限控制,用户访问频率限制
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            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)
        # 6. 二次加工request
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
initial
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.
        # 2. 确定request版本信息
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        # 3. 用户认证
        self.perform_authentication(request)
        # 4. 权限控制
        self.check_permissions(request)
        # 5. 用户访问频率限制
        self.check_throttles(request)
check_throttles
def check_throttles(self, request):
        """
        Check if request should be throttled.
        Raises an appropriate exception if the request is throttled.
        """
        for throttle in self.get_throttles():
            #循环每一个throttle对象,执行allow_request方法
            # allow_request:
                #返回False,说明限制访问频率
                #返回True,说明不限制,通行
            if not throttle.allow_request(request, self):
                self.throttled(request, throttle.wait())
                #throttle.wait()表示还要等多少秒就能访问了

check_throttles
get_throttles
def get_throttles(self):
    """
    Instantiates and returns the list of throttles that this view uses.
    """
    return [throttle() for throttle in self.throttle_classes]
throttle_classes(可自定制)
class APIView(View):

    # 以下策略可以设置为全局策略或按视图策略
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    #这个就是我们可以设置的频率限制
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    # 允许其他设置的依赖注入,以使测试更容易
    settings = api_settings

    schema = DefaultSchema()
总结

如何实现的评率限制

- 匿名用户,用IP作为用户唯一标记,但如果用户换代理IP,无法做到真正的限制(因为可以使用代理)。
- 登录用户,用用户名或用户ID做标识。
具体实现:
	在django的缓存中 = {
        throttle_anon_1.1.1.1:[100121340,],
        1.1.1.2:[100121251,100120450,]
    }


    限制:60s能访问3次
    来访问时:
        1.获取当前时间 100121280
        2.100121280-60 = 100121220,小于100121220所有记录删除
        3.判断1分钟以内已经访问多少次了? 4 
        4.无法访问
    停一会
    来访问时:
        1.获取当前时间 100121340
        2.100121340-60 = 100121280,小于100121280所有记录删除
        3.判断1分钟以内已经访问多少次了? 0
        4.可以访问

3.jwt

解释:JSON Web Token,它是一种认证技术。用在前后端分离项目中,实现和用户登录相关的操作。

在我们dajngo的时候使用的方式是session,在我们上一节的内容中也使用了认证,那种是使用uuid随机生成一个随机码给前端,后端储存一份,用户再来请求的时候携带该token和后端的token对比验证。

一般用户认证有2中方式:

  • token

    用户登录成功之后,生成一个随机字符串,自己保留一分+给前端返回一份。
    
    以后前端再来发请求时,需要携带字符串。
    
    后端对字符串进行校验(是否超时,是否合法)。
    
  • jwt

    用户登录成功之后,生成一个随机字符串,给前端。
    	-格式:xx.xx.xx
    	- 生成随机字符串
    		{typ:"jwt","alg":'HS256'}     {id:1,username:'alx','exp':10}
    		98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928374
    		- token类型和短发通过base64url加密
    		- 用户传输的一些数据通过base64加密
    		- 前两个密文拼接后使用h256加密+加盐(该盐是一串字符串,jwt里面自动处理)再使用base64url进行加密
    		- 给前端返回
    		98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375
    
    前端获取随机字符串之后,保留起来。
    以后再来发送请求时,携带98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375。
    
    
    后端接受到之后,
    	1.先做时间判断
        2.字符串合法性校验。
        
    
安装
pip3 install djangorestframework-jwt
案例
  • app中注册

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'rest_framework',
        #注册
        'rest_framework_jwt'
    ]
    
  • 用户登录

    import uuid
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import URLPathVersioning
    from rest_framework import status
    
    from api import models
    
    class LoginView(APIView):
        """
        登录接口
        """
        def post(self,request,*args,**kwargs):
    
            # 基于jwt的认证
            # 1.去数据库获取用户信息
            from rest_framework_jwt.settings import api_settings
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    
            user = models.UserInfo.objects.filter(**request.data).first()
            if not user:
                return Response({'code':1000,'error':'用户名或密码错误'})
    
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response({'code':1001,'data':token})
    
    
  • 用户认证

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    # from rest_framework.throttling import AnonRateThrottle,BaseThrottle
    
    
    class ArticleView(APIView):
        # throttle_classes = [AnonRateThrottle,]
    
        def get(self,request,*args,**kwargs):
            # 获取用户提交的token,进行一步一步校验
            import jwt
            from rest_framework import exceptions
            from rest_framework_jwt.settings import api_settings
            jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
    
            jwt_value = request.query_params.get('token')
            try:
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                msg = '签名已过期'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.DecodeError:
                msg = '认证失败'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.InvalidTokenError:
                raise exceptions.AuthenticationFailed()
    
            return Response('文章列表')
    
详细使用实例
falsk中使用

jwt_auth.py

import jwt
import datetime
from jwt import exceptions

JWT_SALT = 'iv%x6xo7l7_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+unjv='


def create_token(payload, timeout=20):
    """
    :param payload:  例如:{'user_id':1,'username':'wupeiqi'}用户信息
    :param timeout: token的过期时间,默认20分钟
    :return:
    """
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)
    result = jwt.encode(payload=payload, key=JWT_SALT, algorithm="HS256", headers=headers).decode('utf-8')
    return result


def parse_payload(token):
    """
    对token进行和发行校验并获取payload
    :param token:
    :return:
    """
    result = {'status': False, 'data': None, 'error': None}
    try:
        verified_payload = jwt.decode(token, JWT_SALT, True)
        result['status'] = True
        result['data'] = verified_payload
    except exceptions.ExpiredSignatureError:
        result['error'] = 'token已失效'
    except jwt.DecodeError:
        result['error'] = 'token认证失败'
    except jwt.InvalidTokenError:
        result['error'] = '非法的token'
    return result

manage.py

from flask import Flask, request, jsonify, views, g
from utils.jwt_auth import create_token, parse_payload

app = Flask(__name__)


# 通过url传递token

@app.before_request
def jwt_query_params_auth():
    if request.path == '/login/':
        return

    token = request.args.get('token')
    result = parse_payload(token)
    if not result['status']:
        return jsonify(result)
    g.user_info = result['data']

@app.route('/login/', methods=['POST'])
def login():
    user = request.form.get('username')
    pwd = request.form.get('password')

    # 检测用户和密码是否正确,此处可以在数据进行校验。
    if user == 'wupeiqi' and pwd == '123':
        # 用户名和密码正确,给用户生成token并返回
        token = create_token({'username': 'wupeiqi'})
        return jsonify({'status': True, 'token': token})
    return jsonify({'status': False, 'error': '用户名或密码错误'})

@app.route('/order/', methods=['GET', "POST", "PUT", "DELETE"])
def order():
    print(g.user_info)
    if request.method == 'GET':
        return "订单列表"
    return "订单信息"


if __name__ == '__main__':
    app.run()
drf中使用

路由

from django.conf.urls import url, include

urlpatterns = [
    url(r'^api/(?P<version>\w+)/', include('api.urls')),
]
from django.conf.urls import url, include
from api import views

urlpatterns = [
    #登陆
    url('^login/$', views.LoginView.as_view()),
    #订单
    url('^order/$', views.OrderView.as_view()),
]

视图

from rest_framework.views import APIView
from rest_framework.response import Response

from utils.jwt_auth import create_token
from extensions.auth import JwtQueryParamAuthentication, JwtAuthorizationAuthentication


class LoginView(APIView):
    def post(self, request, *args, **kwargs):
        """ 用户登录 """
        user = request.POST.get('username')
        pwd = request.POST.get('password')

        # 检测用户和密码是否正确,此处可以在数据进行校验。
        if user == 'wupeiqi' and pwd == '123':
            # 用户名和密码正确,给用户生成token并返回
            token = create_token({'username': 'wupeiqi'})
            return Response({'status': True, 'token': token})
        return Response({'status': False, 'error': '用户名或密码错误'})


class OrderView(APIView):
    # 通过url传递token
    authentication_classes = [JwtQueryParamAuthentication, ]

    # 通过Authorization请求头传递token
    # authentication_classes = [JwtAuthorizationAuthentication, ]

    def get(self, request, *args, **kwargs):
        print(request.user, request.auth)
        return Response({'data': '订单列表'})

    def post(self, request, *args, **kwargs):
        print(request.user, request.auth)
        return Response({'data': '添加订单'})

    def put(self, request, *args, **kwargs):
        print(request.user, request.auth)
        return Response({'data': '修改订单'})

    def delete(self, request, *args, **kwargs):
        print(request.user, request.auth)
        return Response({'data': '删除订单'})

from utils.jwt_auth import create_token

import jwt
import datetime
from jwt import exceptions

JWT_SALT = 'iv%x6xo7l7_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+unjv='


def create_token(payload, timeout=20):
    """
    :param payload:  例如:{'user_id':1,'username':'wupeiqi'}用户信息
    :param timeout: token的过期时间,默认20分钟
    :return:
    """
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)
    result = jwt.encode(payload=payload, key=JWT_SALT, algorithm="HS256", headers=headers).decode('utf-8')
    return result


def parse_payload(token):
    """
    对token进行和发行校验并获取payload
    :param token:
    :return:
    """
    result = {'status': False, 'data': None, 'error': None}
    try:
        verified_payload = jwt.decode(token, JWT_SALT, True)
        result['status'] = True
        result['data'] = verified_payload
    except exceptions.ExpiredSignatureError:
        result['error'] = 'token已失效'
    except jwt.DecodeError:
        result['error'] = 'token认证失败'
    except jwt.InvalidTokenError:
        result['error'] = '非法的token'
    return result

from extensions.auth import JwtQueryParamAuthentication, JwtAuthorizationAuthentication

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from utils.jwt_auth import parse_payload


class JwtQueryParamAuthentication(BaseAuthentication):
    """
    用户需要在url中通过参数进行传输token,例如:
    http://www.pythonav.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
    """

    def authenticate(self, request):
        token = request.query_params.get('token')
        payload = parse_payload(token)
        if not payload['status']:
            raise exceptions.AuthenticationFailed(payload)

        # 如果想要request.user等于用户对象,此处可以根据payload去数据库中获取用户对象。
        return (payload, token)


class JwtAuthorizationAuthentication(BaseAuthentication):
    """
    用户需要通过请求头的方式来进行传输token,例如:
    Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
    """

    def authenticate(self, request):

        # 非登录页面需要校验token
        authorization = request.META.get('HTTP_AUTHORIZATION', '')
        auth = authorization.split()
        if not auth:
            raise exceptions.AuthenticationFailed({'error': '未获取到Authorization请求头', 'status': False})
        if auth[0].lower() != 'jwt':
            raise exceptions.AuthenticationFailed({'error': 'Authorization请求头中认证方式错误', 'status': False})

        if len(auth) == 1:
            raise exceptions.AuthenticationFailed({'error': "非法Authorization请求头", 'status': False})
        elif len(auth) > 2:
            raise exceptions.AuthenticationFailed({'error': "非法Authorization请求头", 'status': False})

        token = auth[1]
        result = parse_payload(token)
        if not result['status']:
            raise exceptions.AuthenticationFailed(result)

        # 如果想要request.user等于用户对象,此处可以根据payload去数据库中获取用户对象。
        return (result, token)

django中间件中使用

配置文件

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # 通过url传递token
    'middlewares.jwt.JwtQueryParamMiddleware' 
    # 通过Authorization请求头传递token
    # 'middlewares.jwt.JwtAuthorizationMiddleware'  
]

路由

from django.conf.urls import url
from api import views

urlpatterns = [
    url('login/', views.LoginView.as_view()),
    url('order/', views.OrderView.as_view()),
]

视图

from django.http import JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

from utils.jwt_auth import create_token

#免除csrf认证
@method_decorator(csrf_exempt, name='dispatch')
class LoginView(View):
    def post(self, request, *args, **kwargs):
        """ 用户登录 """
        user = request.POST.get('username')
        pwd = request.POST.get('password')

        # 检测用户和密码是否正确,此处可以在数据进行校验。
        if user == 'wupeiqi' and pwd == '123':
            # 用户名和密码正确,给用户生成token并返回
            token = create_token({'username': 'wupeiqi'})
            return JsonResponse({'status': True, 'token': token})
        return JsonResponse({'status': False, 'error': '用户名或密码错误'})

#免除csrf认证
@method_decorator(csrf_exempt, name='dispatch')
class OrderView(View):

    def get(self, request, *args, **kwargs):
        print(request.user_info)
        return JsonResponse({'data': '订单列表'})

    def post(self, request, *args, **kwargs):
        print(request.user_info)
        return JsonResponse({'data': '添加订单'})

    def put(self, request, *args, **kwargs):
        print(request.user_info)
        return JsonResponse({'data': '修改订单'})

    def delete(self, request, *args, **kwargs):
        print(request.user_info)
        return JsonResponse({'data': '删除订单'})

from utils.jwt_auth import create_token

import jwt
import datetime
from jwt import exceptions

JWT_SALT = 'iv%x6xo7l7_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+unjv='


def create_token(payload, timeout=20):
    """
    :param payload:  例如:{'user_id':1,'username':'wupeiqi'}用户信息
    :param timeout: token的过期时间,默认20分钟
    :return:
    """
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)
    result = jwt.encode(payload=payload, key=JWT_SALT, algorithm="HS256", headers=headers).decode('utf-8')
    return result


def parse_payload(token):
    """
    对token进行和发行校验并获取payload
    :param token:
    :return:
    """
    result = {'status': False, 'data': None, 'error': None}
    try:
        verified_payload = jwt.decode(token, JWT_SALT, True)
        result['status'] = True
        result['data'] = verified_payload
    except exceptions.ExpiredSignatureError:
        result['error'] = 'token已失效'
    except jwt.DecodeError:
        result['error'] = 'token认证失败'
    except jwt.InvalidTokenError:
        result['error'] = '非法的token'
    return result

中间件

from django.utils.deprecation import MiddlewareMixin
from utils.jwt_auth import parse_payload
from django.http import JsonResponse


class JwtQueryParamMiddleware(MiddlewareMixin):
    """
    用户需要在url中通过参数进行传输token,例如:
    http://www.pythonav.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
    """

    def process_request(self, request):
        if request.path_info == '/login/':
            return

        token = request.GET.get('token')
        result = parse_payload(token)
        if not result['status']:
            return JsonResponse(result)
        request.user_info = result['data']


class JwtAuthorizationMiddleware(MiddlewareMixin):
    """
    用户需要通过请求头的方式来进行传输token,例如:
    Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
    """

    def process_request(self, request):

        # 如果是登录页面,则通过
        if request.path_info == '/login/':
            return

        # 非登录页面需要校验token
        authorization = request.META.get('HTTP_AUTHORIZATION', '')
        auth = authorization.split()
        if not auth:
            return JsonResponse({'error': '未获取到Authorization请求头', 'status': False})
        if auth[0].lower() != 'jwt':
            return JsonResponse({'error': 'Authorization请求头中认证方式错误', 'status': False})
        if len(auth) == 1:
            return JsonResponse({'error': "非法Authorization请求头", 'status': False})
        elif len(auth) > 2:
            return JsonResponse({'error': "非法Authorization请求头", 'status': False})

        token = auth[1]
        result = parse_payload(token)
        if not result['status']:
            return JsonResponse(result)
        request.user_info = result['data']
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值