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. Java子类上加lombock注解@Data或者@ToString,日志中不包括父类的属性

    问题描述:Java子类上加lombock注解@Data或者@ToString,在翻阅日志的时候,发现不打印父类的属性. 问题分析:@Data在编译时会自动为实体类添加setter.getter和toS ...

  2. python C3算法

    Python MRO C3算法是python当中计算类继承顺序的一个算法,从python2.3以后就一直使用此算法了. c3 linearization算法称为c3线性化算法 C3算法原理 首先定义几 ...

  3. HTML显示不出来图片原因及解决办法

    HTML插入图片时,一直显示不出来图片,大体分为两种情况. 一.写了代码,也有图片的空间,但就是没有图片. 二.有图片位置,也有图片框框,但图片就是不显示. 针对图一,应该是路径问题,尽量不要有汉字, ...

  4. 接口被刷百万QPS,怎么防?

    大家好,我是苏三. 今天我们不聊风花雪月,只讲这个让无数开发者夜不能寐的终极命题:当恶意流量如海啸般扑来,如何守住你的系统防线? 有些小伙伴在工作中可能经历过接口被刷的噩梦,但百万QPS量级的攻击完全 ...

  5. Spring注解中@Resource和@Authwired的区别

    Spring注解中@Resource和@Authwired的区别 @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 by ...

  6. jq回调函数、jq一个函数执行完毕后再执行另一个函数

    jq回调函数.jq一个函数执行完毕后再执行另一个函数 - small-match - 博客园 (cnblogs.com) jq回调函数第一版 复制代码 <!DOCTYPE html> &l ...

  7. Oracle并发控制

    并发与锁定 当多用户同一时刻访问相同的数据库资源时,将产生并发.并发极易破坏数据的一致性.锁定是处理并发的重要手段,用户在修改某一资源前,必须首先获得资源的修改权.而这种修改权具有排他性. 并发与锁定 ...

  8. MySQL 10 MySQL为什么有时候会选错索引?

    场景引入 我们知道,MySQL中一张表可以支持多个索引.但是写SQL语句时,并没有主动指定使用哪个索引,而是由MySQL来确定.而有时候,MySQL会选错索引,导致执行速度变得很慢. 举个例子,假设一 ...

  9. bluez 遇到了一个bug,其实我一直没有理解bluez的架构

    现在写,bluez的所有的东西. 就当官网那么少的资料的 说明文档好了. 参考文献 http://blog.csdn.net/gatieme/article/details/49358471 http ...

  10. Android List数组列表自定义排序

    自定义排序 例如:根据文件的最后修改时间进行排序,最新文件在前 Collections.sort(lstFiles, new Comparator<FileListData>() { @O ...