• JWT


    什么是JWT?

      json web token,一般用于用户认证(前后端分离/微信小程序/app开发)

      部分公司用的pip install djangorestframework-jwt本质就是调用pyjwt

    传统的token验证工作流程:

    用户登录,后台效验用户名和密码正确会生成一个Token(随机的字符串),会在数据库保存一份,然后将生成的token返回给前端,下次用户访问其他页面会带着token,
    根据这个token去数据库里对比判断用户是否登录。

              

    实现代码:

    # 创建一个Django项目
        我们用restframework来写,注意在settings里的app里要把rest_framework加上
      
    # 创建一个model表,要有token字段

             

    # 写urls

             

    # 在views里写
    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app01 import models
    import uuid
    # Create your views here.
    class LoginView(APIView):
        def post(self,request,*args,**kwargs):
            name = request.data.get('name')
            pwd = request.data.get('password')
            userobj = models.UserInfo.objects.filter(name=name,password=pwd).first()
            if not userobj:
                return Response({'code': 300, 'msg': '用户名密码错误'})
            token = str(uuid.uuid4())
            userobj.token = token
            userobj.save()
            return Response({'code':100,'msg':'登录成功'})
    
    class OrderView(APIView):
        def get(self,request,*args,**kwargs):
            token = request.query_params.get('token')
            print(token,request.query_params)
            if not token:
                return Response({'code':301,'msg':'请登录后访问'})
            user_obj = models.UserInfo.objects.filter(token=token).first()
            if not user_obj:
                return Response({'code':302,'msg':'token失效,请重新登录'})
            return Response('订单页面')
    # 用postman测试

     

    -----------------------------------------------------------------------------------------------------------------------------

    登录成功后会在数据库里产生token

    不带token访问会要求登录后访问

    乱带一个会校验token

    带数据库里的token才可以查看信息

     

    这是传统的token验证的做法

    下面来介绍一下JWT的原理及实现方法

      

        https://jwt.io/#debugger-io

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

    eyJhbGciOiJIUzIINiIsInR5cCI6IkpxvCJ9. eyJzdwIioiIxMjMONTY3ODkwIiwibmFtZSI6IkpvaG4gRG91Iiwi awFOIjoxNTE2MjM5MDIyfQ. Sf1KxwRJSMeKKF 2QT4fwpMeJf36POk6yJV_ adQssw5c

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

    Token的加密过程

    第一段字符串,HEADER,内部包含算法/token类型

    json转化成字符串,然后做base64url加密(base64加密;里面的“+”,“/”等换成“_”)


    {
    "a1g": "HS256", "typ": "JWT" }

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

    json转化成字符串,然后做base64url加密(base64加密;里面的“+”,“/”等换成“_”)

    {
        "id"; "123123",
        "name": "chenggen""exp": 1516239022 #超时时间
    }

    第三段字符串:

    第一步:第1,2部分密文拼接起来
    eyJhbGcioiJIUzIINiIsInR5cCI6IkpXVC19.eyJzdwIioiIxMjMONTY30DkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaNFOIjoxNTE2MjM5MDIyfQ
    
    第二步:对前2部分密文进行HS256加密+加盐
    
    第三步:对HS256加密后的密文再做base64ur1加密

     后端的效验过程

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

    获取token

    第一步:对token进行切割

    eyJhbGcioiJIUzIINiIsInRScCI6IkpXVCJ9. eyJzdwIiOiIxMjMONTY30DkwIiwibmFtZSI6IkpvaG4gRG91Iiwi alwFOIjoxNTE2MjM5MDIyfQ. Sf]KxwRJSMeKKF 2QT4fwpMeJf36POk6yJV_ adQssw5c

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

    {
      "
    id": "123123",   "name": ”chenggen"   "exp": 1516239022 #超时时间
    }

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

    第一步:第1,2部分密文拼接起来
    
    eyJhbGcioiJIUzINiIsInRScCI6IkpXVC39.eyJzdwTi0iTxMjMONTY30DkwIiwibmFtZSI6IkpvaG4gRG9lIiwiainFOIjoxNTE2MjM5MDIyfQ
    
    第二步:对前2部分密文进行HS256加密+加盐
    
    密文= base64解密(sf1KxwRJSMeKKF2QT4fwpMeJf36POk6yJV adQssw5c)如果相等,表示token未被修改过. (认证通过)

     Jwt的应用

    安装:pip install pyjwt
    class JwtLoginView(APIView):
        def post(self,request,*args,**kwargs):
            name = request.data.get('name')
            pwd = request.data.get('password')
            userobj = models.UserInfo.objects.filter(name=name,password=pwd).first()
            if not userobj:
                return Response({'code': 300, 'msg': '用户名密码错误'})
            import jwt
            import datetime
    
            salt = 'deghrtyjtdhtegjdyujhtjhrgtrjttf'
            # 构造header , 这里不写默认的也是
            headers = {
                'typ': 'jwt',
                'alg': 'HS256'
            }
            # 构造payload
            payload = {
                'user_id': userobj.id,  # 自定义用户ID
                'username': userobj.name,  # 自定义用户名
                'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5)  # 超时时间,取现在时间,五分钟后token失效
            }
            token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8')
            return Response({'code':100,'msg':'登录成功','token':token})



    postman 测试

    JWTorder页面

    class JwtOrderView(APIView):
        def get(self,request,*args,**kwargs):
            import jwt
            from jwt import exceptions
            salt = 'deghrtyjtdhtegjdyujhtjhrgtrjttf'
            token = request.query_params.get('token')
            payload = None
            msg = None
    
            try:
                payload = jwt.decode(token,salt,True)
            except exceptions.ExpiredSignatureError:
                msg = 'token已失效'
            except jwt.DecodeError:
                msg = 'token认证失败'
            except jwt.InvalidTokenError:
                msg = '非法的token'
            if not payload:
                return Response({'code':303,'error':msg})
            print(payload['user_id'],payload['username'])
            return Response('订单页面')

    在项目中这么写会很乱,所以我们可以子自定义写文件导入

    urls.py

    url(r'^pro/login/$', views.ProLoginView.as_view()),
    url(r'^pro/order/$', views.ProOrderView.as_view()),

    自定义文件

     auth.py

    from django.conf import settings
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    import jwt
    from jwt import exceptions
    
    class JwtQueryParamsAuthentication(BaseAuthentication):
        def authenticate(self, request):
            # 获取token并判断token的合法性
            token = request.query_params.get('token')
            salt = settings.SECRET_KEY
            try:
                payload = jwt.decode(token,salt,True)
            except exceptions.ExpiredSignatureError:
                raise AuthenticationFailed({'code':1003,'error':'token已失效'})
            except jwt.DecodeError:
                raise AuthenticationFailed({'code':1003,'error':'token认证失败'})
            except jwt.InvalidTokenError:
                raise AuthenticationFailed({'code':1003,'error':'非法的token'})
            # 三种操作
             #1.抛出异常,后续不在执行
             #2.return一个元组(1,2),认证通过,在视图中如果调用request.user就是元组的第一个值,requset.auth就是第二个值
             #3.None
            return (payload,token)

    jwt_auth.py

    import jwt
    import datetime
    from django.conf import settings
    
    def create_token(payload,timeout=5):
        salt = settings.SECRET_KEY
        # 构造header
        header = {
            'typ':'jwt',
            'alg':'HS256'
        }
        # 构造payload
        payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)
        token = jwt.encode(payload=payload,key=salt,algorithm="HS256",headers=header).decode('utf-8')
        return token

    views.py

    from app01.extensions.auth import JwtQueryParamsAuthentication
    from app01.utils.jwt_auth import create_token
    class ProLoginView(APIView):
        def post(self,request,*args,**kwargs):
            name = request.data.get('name')
            pwd = request.data.get('password')
            user_obj = models.UserInfo.objects.filter(name=name,password=pwd).first()
            if not user_obj:
                return Response({'code':301,'error':'用户名或密码错误'})
            token = create_token({'id':user_obj.id,'name':user_obj.name})
            return Response({'code':302,'data':token})
    
    class ProOrderView(APIView):
        authentication_classes = [JwtQueryParamsAuthentication,]
        def get(self,request,*args,**kwargs):
            print(request.user)
            return Response('订单列表')

    最后在settings里设置一个全局的认证

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

    这样写的话全局都会有token认证,但是登陆页面我们不能验证,否则会死循环了,只需要加

    authention_classes = []
  • 相关阅读:
    域名和dns
    Oracle版本区别及版本选择!
    并发
    URL和URI的区别??
    sshpass免密码(免交互)连接
    python之路 目录
    awk sed 总结
    aiohttp使用
    Mac破解软件 “XXX”意外退出 奔溃解决方法
    我的Mac中毒了,病毒居然叫做MacPerformance
  • 原文地址:https://www.cnblogs.com/tuzaizi/p/13282028.html
Copyright © 2020-2023  润新知