JWT原理介绍

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

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

JWT的构成

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

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

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

header

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使用流程最核心的是:

签发:登录接口签发
认证:认证类认证

base64编码和解码

base64 可以把字符串编码成base64的编码格式:(大小写字母,数字和 =)eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogImxxeiIsICJhZG1pbiI6IHRydWV9

base64可以把base64编码的字符串,解码回原来的格式

应用场景

jwt中使用
网络中传输字符串就可以使用base64编码
网络中传输图片,也可能使用base64的编码

base64编码和解码的过程

base64编码

import json
import base64 d = {'name': 'zxr', 'userid': 1, 'age': 18}
info = json.dumps(d)
# print(info) # {"name": "zxr", "userid": 1, "age": 18} # 把字符串转成bytes格式的俩种方式
# 第一种:info.encode('utf-8')
# res = base64.b64encode(info.encode('utf-8'))
# print(res) # b'eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9' # 第二种:强转
print(bytes(info, encoding='utf-8')) # b'{"name": "zxr", "userid": 1, "age": 18}'

base64解码过程

import json
import base64 d = {'name': 'zxr', 'userid': 1, 'age': 18}
info = json.dumps(d)
b'eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9' res = base64.b64decode('eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9')
print(res) # b'{"name": "zxr", "userid": 1, "age": 18}'

将图片保存起来

import json
import base64
d = {'name': 'lqz', 'userid': 6, 'age': 19}
info = json.dumps(d)
print(info)
# 把字符串使用base64编码
res=base64.b64encode(info.encode('utf-8'))
print(res) # eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9
res=base64.b64decode(s)
with open('code.png','wb') as f:
f.write(res)

drf-JWT快速使用

jwt:签发(登录接口) 认证(认证类)

django中使用jwt

# 可以自己写
https://github.com/jpadilla/django-rest-framework-jwt (比较老)
https://github.com/jazzband/djangorestframework-simplejwt (比较新)

安装

pip3 install djangorestframework-jwt

快速使用

1)迁移表,因为它默认使用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"
}

5)认证(认证类):导入,配置在视图类上

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication class TestView(APIView):
authentication_classes = [JSONWebTokenAuthentication, ]
# 是否登录的权限类
permission_classes = [IsAuthenticated, ]
def get(self, request):
return Response('ok')

6)前端访问时,token需要放在请求头中

Authorization:jwt token串

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

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',
}

以后登录接口返回的格式就是咱们写的函数的返回值,前提是auth_user表才可以这么做

自定义user表,签发token

view.py

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication from app01.models import Userinfo
from rest_framework.exceptions import APIException from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
# 自己基于自定义的用户表签发token
class UserView(APIView):
def post(self, request):
try:
username = request.data.get('username')
password = request.data.get('password')
user = Userinfo.objects.get(username=username, password=password)
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({'code': 100, 'msg': '登录成功', 'token': token})
except Exception as e:
raise APIException('用户表名或密码错误')

urls.py

from django.contrib import admin
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token from app01 import views urlpatterns = [
path('user/login/', views.UserView.as_view()),
]

models.py

from django.db import models

# Create your models here.
class Userinfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)

自定义认证类,验证token

view.py

class JWTAuthentication(BaseAuthentication):
def authenticate(self, request):
# 放到头中:token-->HTTP_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_id = payload.get('user_id')
user = UserInfo.objects.filter(pk=user_id).first() # 每次都要查数据库,效率不太好
return (user, jwt_value)
# return (payload,jwt_value)

额外小知识

请求头中:X_FORWARDED_FOR 代指什么?

X-Forwarded-For 是一个 HTTP 扩展头部,主要是为了让 Web 服务器获取访问用户的真实 IP 地址,但是这个IP却未必是真实的,我们后面会回来描述这个问题。一些开发者为了获取客户IP,我们经常会使用request.remote_ip来获得用户IP。但是很多用户都是通过代理来访问服务器的,如果使用remote_ip这个全局变量来获取IP,开发者往往获得的是代理服务器的IP,并不是用户真正的IP。

"客户端=>正向代理=>透明代理=>服务器反向代理=>Web服务器"

集群与分布式

集群:多个人在一起做同样的事,也就是说,同一个业务部署在多个服务器上

分布式:多个人在一起做不同的事,也就是说,一个业务拆分多个子业务部署在多个服务器上

集群主要的使用场景是为了分担请求的压力,但是,当压力增大的时候,可能需要存储的部分,比如mysql无法面对大量的写操作,因为MySQL做成集群后,主要的写压力还是在master(主机)的机器上,其他slave(从机)机器无法分担写压力,这时就引出了“分布式”!

分布式解决了中心化管理的问题。把所有的任务叠加到一个节点处理,效率太慢,所以把一个大的问题拆分成多个小问题,并分别解决,最终协同合作。
分布式主要工作:分解任务,把任务拆解。 # 再深入理解一下集群和分布式及其区别: 分布式:把一个大业务拆分成多个子业务,每个子业务都是一套独立的系统,子业务之间相互协作最终完成整体的大业务。 集群:把处理同一个业务的系统部署多个节点 。 把一套系统拆分成不同的子系统部署在不同服务器上,这叫分布式。 把多个相同的系统部署在不同的服务器上,这叫集群。部署在不同服务器上的相同系统必然要做“负载均衡”。 集群主要是简单加机器解决问题,对于问题本身不做任何分解。 分布式处理里必然涉及任务分解与答案归并。分布式中的某个子任务节点,可以是一个集群,该集群中的任一节点都作为一个完整的任务出现。 集群和分布式都是由多个节点组成,但集群中各节点间基本不需要通信协调,而分布式中各个节点的通信协调是必不可少的。

JWT、base64编码和解码、drf-jwt快速使用、drf-jwt修改返回格式、自定义user表,签发token的更多相关文章

  1. NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计算字符串长度,汉字算两个字符 2019周笔记(2.18-2.23) Mysql语句中当前时间不能直接使用C#中的Date.Now传输 Mysql中Count函数的正确使用

    NET MVC全局异常处理(一)   目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关 ...

  2. Kotlin/Java Base64编码和解码(图片、文件)

    原文: Kotlin/Java Base64编码和解码(图片.文件) | Stars-One的杂货小窝 最近在项目中使用到了Base64编码和解码,便是稍微写篇文章记录一下 PS:本文代码都是使用Ko ...

  3. BASE64编码和解码(VC源代码) 并 内存加载 CImage 图像

      BASE64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本.完整的BASE64定义可见 RFC1421和 RFC2045.编码后的数据比原始数据略长,为原来的4/3.在电子 ...

  4. base64编码、解码的C语言实现

    转自:http://www.cnblogs.com/yejianfei/archive/2013/04/06/3002838.html base64是一种基于64个可打印字符来表示二进制数据的表示方法 ...

  5. android Java BASE64编码和解码二:图片的编码和解码

    1.准备工作 (1)在项目中集成 Base64 代码,集成方法见第一篇博文:android Java BASE64编码和解码一:基础 (2)添加 ImgHelper 工具类 package com.a ...

  6. Python中进行Base64编码和解码

    Base64编码 广泛应用于MIME协议,作为电子邮件的传输编码,生成的编码可逆,后一两位可能有“=”,生成的编码都是ascii字符.优点:速度快,ascii字符,肉眼不可理解缺点:编码比较长,非常容 ...

  7. Javascript Base64编码与解码

    原文:[转]Javascript Base64编码与解码 <html> <head> <META HTTP-EQUIV="MSThemeCompatible&q ...

  8. java使用Base64编码和解码的图像文件

    1.编码和解码下面的代码示例看: import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import j ...

  9. C# base64编码、解码

    public class TransferCode { #region base-64编码.解码 /// <summary> /// BASE64编码 /// </summary&g ...

  10. Url的Base64编码以及解码

    Base64可以将二进制转码成可见字符方便进行http传输,但是base64转码时会生成“+”,“/”,“=”这些被URL进行转码的特殊字符,导致两方面数据不一致.我们可以在发送前将“+”,“/”,“ ...

随机推荐

  1. 【uni-app】在windows10系统中HBuliderX用iPhone苹果手机进行调试运行详细说明

    测试准备: 1)iphone13  ios18.4.1  和一根可以读取数据的苹果线 2)HBuliderX打开uni-app项目文件(项目图标是正方形内一个U) 3)windows10系统 测试目标 ...

  2. odoo里面的动作

    来源:Odoo中的五种action都是继承自ir.actions.actions模型实现的子类,共有五种,下面会一个一个给出具体例子 1.链接Action(ir.actions.act_url):ta ...

  3. Git回滚命令reset|回退代码到某次commit

    摘要:使用Git回退命令reset回退代码到某次commit.   需求背景: 需要回滚提交的代码的场景常常是如下三个: 提交代码到错误Git分支: 提交的代码不需要上线了,而同一分支有需要上线的代码 ...

  4. 「Note」您想来点数据结构吗?

    大分块系列 最初分块 \(\color{black}{P4119}\) 考虑数列分块+值域分块 数列分块需要维护: \(nid_{i,j}\) \(fid_i\) \(f_i\) 块 \(i\) 中数 ...

  5. 【深入理解Spring AOP】核心原理与代理机制详解

    深入理解Spring AOP:核心原理与代理机制详解 引言 在现代Java开发中,面向切面编程(AOP)已经成为解决横切关注点的主流方案.作为Spring框架的核心模块之一,Spring AOP通过代 ...

  6. 袋鼠云:基于Flink构建实时计算平台的总体架构和关键技术点

    数栈是云原生-站式数据中台PaaS,我们在github和gitee上有一个有趣的开源项目:FlinkX,FlinkX是一个基于Flink的批流统一的数据同步工具,既可以采集静态的数据,也可以采集实时变 ...

  7. 1-python中的两大法宝和加载数据

    python中的两大法宝和加载数据 1. Python两大法宝 ① Python3.6.3相当于一个package,package里面有不同的区域,不同的区域有不同的工具. ② Python语法有两大 ...

  8. C++开发的一些周边能力

    go在后端开发中逐渐替代了C/C++,为什么大家更喜欢用go之类的新语言呢? C/C++作为上世纪的古老语言,很多地方没有针对程序员体验来考虑人性化编程.举几个例子: 1.windows.linux. ...

  9. java类型转换的例子

    Java中数据类型自动转换优先级 犹由于java是强类型语言,所以进行一些运算的时候,必须进行类型转换运行中不同类型数据先转为同一类型再进行运算 /*注意点:1.不能对布尔值进行转换2.不能把对象类型 ...

  10. Redis 在windows 下安装使用

    管理界面: Release 2022.5 · lework/RedisDesktopManager-Windows (github.com)redis 服务:发布 ·特波拉多夫斯基/雷迪斯 (gith ...