JWT 实现

1. 基于传统的JWT认证

2. jwt

1. 基于传统的token认证

  用户将用户名和密码发送给服务端,服务端返回其token值(uuid),并将其token值保存入库,以后用户再登录时需要携带token值进行验证。

例:login

class Login(APIView):
    def post(self, request, *args, **kwargs):
        '''
        获取用户名密码
        与数据库是否一致
        一致,生成uuid()值,保存数据库,返回给前端
        '''
        username = request.data.get('name')
        passwd = request.data.get('passwd')
        user_object = models.User_info.objects.filter(username=username, password=passwd).first()
        if not user_object:
            return Response({'code': 1001, 'error': '用户名或密码输入有误'})
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response({'code': 200, 'data': random_string})
传统token1,认证 Django

例:order

class Order(APIView):
    def get(self, request):
        '''
        query_params   # 获取url中的token值
        '''
        token = request.query_params.get('token')
        if not token:
            return Response({'code': 2002, 'error': '登录成功后才能访问'})
        user_obj = models.User_info.objects.filter(token=token).first()
        if not user_obj:
            return Response({'code': 2001, 'error': 'token不正确'})
        return Response({'code': 200, 'data': '订单列表'})
传统认证token2

2. jwt

  用户登录,服务端返回token值(服务端不保存),以后用户再来访问需要携带token,服务端获取token,再做token检验。

  优势: 相较于传统的jwt服务端不需要进行保存。

3. jwt 实现过程

  用户提交用户名和密码给服务端,如果登录成功,使用jwt创建一个token,并给用户返回。

eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.    # 第一部分
eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ.  # 第二部分
n9f7gShAedaVVhq2dkA8ClpJ5TI1AhCogalSqOLCjuU   # 第三部分

注意:jwt 生产的token是由三段字符串组成,并用 . 连接起来。

  第一段字符串,HEADER, 内部包含算法 (默认哈希256)/和token类型。

  json转化成字符串,然后做base64url加密(base64加密;+ _)

# 构造header
headers = {
    'typ': 'jwt',
    'alg': 'HS256'
}

  第二段字符串,payload,自定义值

  json转化成字符串,然后做base64url加密(base64加密;+ _)

# 构造payload
payload = {
    'user_id': user_obj.id,  # 自定义用户ID
    'username': user_obj.username,  # 自定义用户名
    'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)  # 超时时间
}        

  第三段字符串

第一步:第1,2部分密文拼接起来
eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ

第二步:对前2部分密文进行HS256加密 + 加盐

第三部:对HS256加密后的密文再做base65url 加密

以后用户再来访问的时候,需要携带token,后端需要对token进行校验,获取token

  第一步:对token进行切割

eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.    # 第一部分
eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ.  # 第二部分
n9f7gShAedaVVhq2dkA8ClpJ5TI1AhCogalSqOLCjuU   # 第三部分

  第二步:对第二部分进行base64url解密,并获取payload信息,检测token是否超时?

payload = {
    'user_id': user_obj.id,  # 自定义用户ID
    'username': user_obj.username,  # 自定义用户名
    'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)  # 超时时间
}    

  第三步:把第1,2端拼接,再次执行sha256加密。

第一步:第1,2部分密文拼接起来
eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ

第二步:对前2部分密文进行HS256加密 + 加盐

密文 = base64解密(n9f7gShAedaVVhq2dkA8ClpJ5TI1AhCogalSqOLCjuU)

例: 代码演示

pip install pyjwt
安装 jwt
from rest_framework.response import Response
from rest_framework.views import APIView
class Jwt_login(APIView):
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        SALT = 'qpo0rerngnejg3n5j3nk2'
        if not all([username, password]):
            return Response({'code': 10010, 'error': '输入完整的用户名密码'})
        user_obj = models.User_info.objects.filter(username=username, password=password).first()
        if not user_obj:
            return Response({'code': 10020, 'error': '用户名密码不正确'})
        # 构造header
        headers = {
            'typ': 'jwt',
            'alg': 'HS256'
        }
        # 构造payload
        payload = {
            'user_id': user_obj.id,  # 自定义用户ID
            'username': user_obj.username,  # 自定义用户名
            'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)  # 超时时间
        }
        result = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
        return Response({'code': 200, 'data': result})
login 代码演示
from jwt import exceptions
class Jwt_order(APIView):
    def get(self, request, *args, **kwargs):
        token = request.query_params.get('token')
        SALT = 'qpo0rerngnejg3n5j3nk2'
        msg = None
        if not token:
            return Response({'code': 3001, 'error': 'token值不能为空'})
        try:
            payload = jwt.decode(token, SALT, True)
            return Response({'code':200,'data':payload})
        except exceptions.ExpiredSignatureError:
            msg = 'token已失效'
        except jwt.DecodeError:
            msg = 'token认证失败'
        except jwt.InvalidTokenError:
            msg = '非法的token'
        return Response({'code':20020,'data':msg})
order 代码演示

例: 代码演示2升级

# 在子应用中新建文件夹,新建py,将返回token值的代码写入

from app01.utils.jwt_auth import create_token
class Pro_login(APIView):
    authentication_classes = []
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        if not all([username, password]):
            return Response({'code': 10010, 'error': '输入完整的用户名密码'})
        user_obj = models.User_info.objects.filter(username=username, password=password).first()
        if not user_obj:
            return Response({'code': 10020, 'error': '用户名密码不正确'})
        token = create_token({'id':user_obj.id,'name':user_obj.username},1)
        return Response({'code': 200, 'data': token})
views.py(login)
# 子应用返回token代码如下

from django.conf import settings
import jwt
import datetime
from rest_framework.response import Response
def create_token(payload,timeout):
    SALT = settings.SECRET_KEY
    # 构造header
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    # 构造payload
    payload['exp'] =  datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)  # 超时时间
    result = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
    return result
jwt_auth.py
# 在子应用新建文件夹,新建py,将token认证的代码写入

from app01.extensions.auth import JwtQueryParamsAuthentication
class Pro_order(APIView):
    authentication_classes = [JwtQueryParamsAuthentication, ] # 类似于Django中间件,先执行这个
    def get(self, request,*args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('订单列表')
views.py(order)
from django.conf import settings
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response
import jwt
from jwt import exceptions
'''
获取token并判断token的合法性
1. 切割
2. 解密第二段/判断过期
3. 验证第三段的合法性
'''
class JwtQueryParamsAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get('token')
        SALT = settings.SECRET_KEY
        if not token:
            raise AuthenticationFailed({'code':'token不存在'})
        try:
            payload = jwt.decode(token, SALT, True)
        except exceptions.ExpiredSignatureError:
            raise AuthenticationFailed({'code':1,'error':"token已失效"})
        except jwt.DecodeError:
            raise AuthenticationFailed({'code':2,'error':"token认证失败"})
        except jwt.InvalidTokenError:
            raise AuthenticationFailed({'code':3,'error':"非法的token"})

        return (payload, token)


    # 三种操作
    # 1. 抛出异常,后续不执行
    # 2. return一个元组 (1,2),认证通过,在视图中如果调用request.user就是调用第一个值,request.auth就是元祖的第二个值
    # 3. None 表示下一次认证
auth.py

例:代码演示3 全局

from app01.utils.jwt_auth import create_token
class Pro_login(APIView):
    authentication_classes = []   # j加入后表示不受全局限制
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        if not all([username, password]):
            return Response({'code': 10010, 'error': '输入完整的用户名密码'})
        user_obj = models.User_info.objects.filter(username=username, password=password).first()
        if not user_obj:
            return Response({'code': 10020, 'error': '用户名密码不正确'})
        token = create_token({'id':user_obj.id,'name':user_obj.username},1)
        return Response({'code': 200, 'data': token})



from app01.extensions.auth import JwtQueryParamsAuthentication
class Pro_order(APIView):
    # authentication_classes = [JwtQueryParamsAuthentication, ] # 类似于Django中间件,先执行这个
    def get(self, request,*args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('订单列表')
View.py

用户登录返回token,与认证token不变。

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES" : ["app01.extensions.auth.JwtQueryParamsAuthentication", ]
}

# 在APIView 中底层有DEFAULT_AUTHENTICATION_CLASSES方法。
settings

图片路径如下:

posted @ 2020-11-16 21:03  Mr-刘  阅读(325)  评论(0编辑  收藏  举报