首页 > 其他分享 >JWT的介绍和使用

JWT的介绍和使用

时间:2022-10-28 17:03:50浏览次数:48  
标签:username JWT jwt 介绍 token user 使用 import payload

JWT的含义

Json web token(JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(sso)场景,JWT的声明一般被用来在身份提供者和服务者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其他业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密

token的应用于web方向的称之为jwt

image

image

image

JWT的构成

JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature)。

jwt的头部承载两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:

{
  'typ': 'JWT',
  'alg': 'HS256'
}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

payload

荷载就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

signature

JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

JWT原理和校验过程

原理

1)jwt分三段式:头.体.签名 (head.payload.sgin)
2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
	"company": "公司信息",
	...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
	"user_id": 1,
	...
}
6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
	"head": "头的加密字符串",
	"payload": "体的加密字符串",
	"secret_key": "安全码"
}

校验过程

1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户

drf项目的jwt认证开发流程

使用的是django_rest_framework里面的jwt模块

1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中

2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用

drf-jwt安装与使用

安装

pip3 install djangorestframework-jwt

使用

1)迁移表,表需要继承auth里内置的user表,因为它默认使用auth的user表签发token
2)创建超级用户(auth的user表中要右记录)
3)不需要写登录接口了,如果是使用auth的user表作为用户表,它可以快速签发
4)签发(登录):只需要在路由中配置(因为它帮咱们写好登录接口了)

from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
    path('login/', obtain_jwt_token),
]

# 返回结果
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inp4ciIsImV4cCI6MTY2NTU3MjU4OSwiZW1haWwiOiJ6eHJAMTYzLmNvbSJ9.0jDd56Jk04-SdZ4AchLHkcfLECS1RvhFwAQ8VoNKiMM"
}

当登录127.0.0.1:8000/login/时采用post请求,然后携带登录的用户名和密码的信息,服务器会自动响应,返回一个token给前端,效果图如下:
image

内置认证类的使用

在需要添加认证的视图函数接口上添加以下配置:

from rest_framework.views import APIView
from rest_framework.response import Response
# Create your views here.
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated

class BookView(APIView):
    authentication_classes = [JSONWebTokenAuthentication,]
    permission_classes = [IsAuthenticated, ]
    def get(self,request):
        return Response('ok')

    def post(self,request):
        return Response('ok11')

添加之后,在访问该视图时,token需要放在请求头中,并且格式为Authorization:jwt token串

Authorization:jwt token串

# 例如:
    Authorization:jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inp4ciIsImV4cCI6MTY2NTU3MzMzMCwiZW1haWwiOiJ6eHJAMTYzLmNvbSJ9.DAX1wLhHYnpGwVzGvMnSFzUudkPgXsYvlrfk-XFdSAc

注意:上述请求头的key必须位Authorization,值为jwt+空格+token串,如果想要修改jwt可以在配置文件中进行如下配置:

image

自定义auth认证类的使用
在一个py文件夹里定义如下一个认证类

from rest_framework_jwt.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from app01.models import UserInfo

# 写一个类继承自BaseAuthentication,重写authenticate方法
class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # token在请求头的值为token-->HTTP_TOKEN   token在请求头的值为Authorization时--->HTTP_AUTHORIZATION
        # print(request.META)
        jwt_value = request.META.get('HTTP_TOKEN')
        # 验证token是否合法,jwt模块下一定有个验证token的函数
        try:
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            raise AuthenticationFailed('token过期了')
        except jwt.DecodeError:
            raise AuthenticationFailed('token解码失败')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('认证失败')
        # 执行到这,说明token合法,payload可以使用
        user = payload.get('username')
        user = UserInfo.objects.filter(username=user).first()  # 每次都要查数据库,效率不太好
        return (user, jwt_value) #user对应了request.user  jwt_value对应了request.auth
        # return (payload,jwt_value)

在需要认证的视图上进行局部配置自己的写的认证类

class BookView(APIView):
    authentication_classes = [JWTAuthentication,]
    permission_classes = [IsAuthenticated, ]
    def post(self,request):
        print(request.user,request.auth)
        return Response('ok11')

drf-jwt修改返回格式

登录成功后,前端看到的格式,太固定了,只有token,我们想做成:

{code:100,msg:'登录成功',token:adfasdfasdf}

固定写法:

写一个函数,函数返回什么,前端就看到什么,然后需要在配置文件中配置一下自己写的这个函数

写一个函数

def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'code': 100,
        'msg': '登录成功',
        'username': user.username,
        'token': token
    }

将函数配置在配置文件中

# djangorestframework-jwt的配置,这个配置了以后优先使用这个
JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_response_payload_handler',
}

image

手动签发token(多方式登录)

使用用户名,手机号,邮箱,都可以登录
前端需要传的数据格式

{
"username":"lqz/1332323223/[email protected]",
"password":"lqz12345"
}

视图

from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin, ViewSet

from app02 import ser
class Login2View(ViewSet):  
    def login(self, request, *args, **kwargs):
        # 1 需要 有个序列化的类
        login_ser = ser.LoginModelSerializer(data=request.data,context={'request':request})
        # 2 生成序列化类对象
        # 3 调用序列号对象的is_validad
        login_ser.is_valid(raise_exception=True)
        token=login_ser.context.get('token')
        # 4 return
        return Response({'status':100,'msg':'登录成功','token':token,'username':login_ser.context.get('username')})

序列化类

from rest_framework import serializers
from api import models
import re
from rest_framework.exceptions import ValidationError

from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
class LoginModelSerializer(serializers.ModelSerializer):
    username=serializers.CharField()  # 重新覆盖username字段,数据中它是unique,post,认为你保存数据,自己有校验没过
    class Meta:
        model=models.User
        fields=['username','password']

    def validate(self, attrs):

        print(self.context)

        # 在这写逻辑
        username=attrs.get('username') # 用户名有三种方式
        password=attrs.get('password')
        # 通过判断,username数据不同,查询字段不一样
        # 正则匹配,如果是手机号
        if re.match('^1[3-9][0-9]{9}$',username):
            user=models.User.objects.filter(mobile=username).first()
        elif re.match('^.+@.+$',username):# 邮箱
            user=models.User.objects.filter(email=username).first()
        else:
            user=models.User.objects.filter(username=username).first()
        if user: # 存在用户
            # 校验密码,因为是密文,要用check_password
            if user.check_password(password):
                # 签发token
                payload = jwt_payload_handler(user)  # 把user传入,得到payload
                token = jwt_encode_handler(payload)  # 把payload传入,得到token
                self.context['token']=token
                self.context['username']=user.username
                return attrs
            else:
                raise ValidationError('密码错误')
        else:
            raise ValidationError('用户不存在')

配置签发过期时间

# jwt的配置
import datetime
JWT_AUTH={
    'JWT_RESPONSE_PAYLOAD_HANDLER':'app02.utils.my_jwt_response_payload_handler',
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间,手动配置
}

标签:username,JWT,jwt,介绍,token,user,使用,import,payload
From: https://www.cnblogs.com/suncolor/p/16832338.html

相关文章

  • 如何在uniapp中优雅地使用WebView
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助从webview页面传值到uniapp中官方文档已经很详细了,这里给大家上我的实战代码,首先在webview页面中引入相......
  • Istio 使用端口列表
    数据平面端口控制平台端口参考文档https://istio.io/latest/docs/ops/deployment/requirements/#ports-used-by-istio......
  • 智能分析网关使用教程:如何在EasyCVR视频融合平台配置告警与抓拍?
    AI智能分析网关设备内置多种深度学习算法,可支持对接入的多路视频流进行智能检测、智能识别等,包括人脸检测与识别、车辆检测与识别、车牌识别、烟火识别、安全帽识别、区域入......
  • EasyCVR使用大华SDK接入时录像显示失败是什么原因?该如何解决?
    EasyCVR视频融合云服务支持多协议、多类型设备的接入,包括国标GB/T28181、RTMP、RTSP/Onvif协议,以及厂家私有协议,如:海康SDK、大华SDK、海康Ehome等。平台可对前端接入的设备......
  • 云小课|MRS基础原理之Hudi介绍
    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说)、深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云。更多精彩内容请单击......
  • 为什么 Mac 更新 Ventura 13.0 后 ssh 无法正常使用?
    为什么Mac更新Ventura13.0后ssh无法正常使用?这个既包含ssh登陆服务器,也包含git的ssh。很多同学更新系统之后,使用这两者都会遇到一条报错,类似于:Permissiond......
  • 使用 Excel 快速拼接 sql 语句
    1.在数据库中查询出要删除的记录的关键字段  selectcol1,col2,col3,col4fromtabName; 2.将结果copy到excel中   3.在excel的E1单元格写如下内容="delet......
  • 使用 WcFTestClient.exe 调试 wcf 服务
    一、文件位置如下C:\ProgramFiles\MicrosoftVisualStudio10.0\Common7\IDE\WcfTestClient.exe二、使用方法添加服务调用......
  • uniapp开发使用 web-view APP 与 H5 (vue)通信
    需求:这边是uniapp开发的APP 需要内嵌H5(vue),就得使用web-view跳转网页H5端在vue的index,html文件引入web-view的插件<scripttype="text/javascript"src="https://js......
  • netty 使用ByteToMessage解决半包粘包
    其实netty所有自带的decoder内置类都是继承与ByteToMessage的。我们想要自定义自己的decoder就继承ByteToMessgae类就可以了。前提:一般发送报文报文中都会有数据的长度,......