JWT

什么是JWT?

json web token,一般用于用户认证(前后端分离/微信小程序/app开发)
部分公司用的djangorestframework-jwt的本质就是调用pyjwt

传统token验证的工作流程

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

img

下面我们用代码来实现功能

创建一个Django项目
我们用restframework来写,注意在settings里的app里要把rest_framework加上

img
创建一个model表,要有token字段
img
views.py里

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测试
img
登录成功后会自动产生一个token,在访问order页面,如果不带token访问会被阻拦,提示登陆后访问。
img
乱带一个token也不行,与数据库里的token不一致会提示token失效
img
只有带这数据库里保存的tokrn来访问页面才会返回数据。
这是传统的实现手法,下面来介绍一下JWT的原理和实现方法

JWT的原理和实现方法

官网:https://jwt.io/#debugger-io
img
用户提交用户名和密码给服务端如果登录成功,使用jwt创建一个token,并给用户返回

eyJhbGciOiJIUzIINiIsInR5cCI6IkpxvCJ9.
eyJzdwIioiIxMjMONTY3ODkwIiwibmFtZSI6IkpvaG4gRG91IiwiawFOIjoxNTE2MjM5MDIyfQ.
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 测试
img

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('订单页面')

自定义认证模块

在项目中这么写会很乱,所以我们可以子自定义写文件导入
img
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

view.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 = []
全部评论

相关推荐

感觉这一周太梦幻了,就像一个梦,很不真实~~~感觉这个暑期,我的运气占了99成,实力只有百分之一4.15上午 腾讯csig 腾讯云部门,面完秒进入复试状态4.16下午 美团优选供应链部门,4.18上午发二面4.17晚上 阿里国际一面,纯拷打,面完我都玉玉了4.18下午 阿里国际二面,是我们leader面的我,很轻松~~4.18晚上 约了hr面4.19上午 hr面,下午两点口头oc4.19晚上 意向书说起来我的暑期好像一次都没挂过~~~~~难道我是天生面试圣体?----------------------------------------------------------------------六个月前,我还是0项目0刷题,当时想的是先把论文发出来再去找实习。结果一次组会,老师打破了我的幻想(不让投B会,只让投刊或者A)我拿头投啊!!!然后就开始物色着找实习,顺便做完了mit的6.s081,但是基本上还是没刷过题目-----------------------------------------------------------------------11月  一次偶然的机会,面进了某个耳机厂的手环部门,大概是做嵌入式的,用的是CPP。12月 莫名其妙拿到了国创的面试机会,0基础四天速成java基础!居然也给我面过了hhhhh,可能是面试没写题吧入职国创后的几个月,一直没活,天天搁那看剧,都快忘了还有暑期实习这回事了~~~~命运的齿轮在2.26开始转动,因为这一天美团开了,我开始慌了,因为那时的我什么都不会。lc,八股,sql全部是0进度。然后就开始了女娲补天,上班刷题,下班继续做之前的开源,顺便学一学八股。3月到现在,lc也刷到快200了,一天最多提交了47次~~~~~~~~~~八股根据别人的面经总结和博客,写了快十万字的笔记~~~~~~~~~~简历上的实习经历和开源,也努力去深挖了,写了几万字的记录~~~~~~所以面试的时候,基本上都能cover了,面试官问到的基础基本都会,不基础的我就把他往我会的地方引。结果好像还不错,基本上每个面试官评价都挺好的emmmmmmmm
投递阿里巴巴等公司10个岗位
点赞 评论 收藏
转发
点赞 1 评论
分享
牛客网
牛客企业服务