首页 > 其他分享 >JWT

JWT

时间:2024-04-21 22:12:19浏览次数:24  
标签:username get self JWT token user serializer

JWT介绍

​ 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token(本质就是token)认证机制。

image-20240418171423016

构成和工作原理

JWT的构成

​ JWT就是一长串字符串,被.分成三段,分别是头部,载荷,签名

​ jwt的头部承载两部分的信息,头部会进行base64的转码

  1. 声名类型,标识这里是jwt
  2. 声名加密的算法 通常是HMAC SHA256
# header
{
  'typ': 'JWT',
  'alg': 'HS256'
}

# 转码后
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

payload

​ 载荷就是存放有效信息的地方,它也会被进行base64转码

# payload
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

# 转码后
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature

​ 签名是用来验证前两个部分有没有被篡改过,它由三部分组成,这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分

  1. header (base64过后)
  2. payload(base64过后)
  3. secret(盐)

本质原理

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-将基本信息字典转成json字符串,再进行base64转码,得到头字符串
2-将用户信息字典转成json字符串,再进行base64转码,得到载荷字符串
3-将头和荷载字符串打包成json字典字符串,再按头字符串的加密方式加密,得到签名字符串

校验

# 根据客户端带token的请求反解析出user对象
1-将token分成三段
2-将第二段的用户信息解析出来,从用户表得到登录用户
3-将第一段和第二段加密 再与第三段比较,一致则校验通过,否则校验不通过

drf项目的jwt认证开发流程

1-浏览器发请求带账号密码,登录接口通过校验,签发token给浏览器,浏览器保存到自己的cookies里面
2-浏览器带token发请求,drf认证组件反解出用户对象,

注意:灯笼裤几口需要做 认证 + 权限 两个局部禁用

base64编码解码

import base64
import json
dic_info={
  "sub": "1234567890",
  "name": "lqz",
  "admin": True
}
byte_info=json.dumps(dic_info).encode('utf-8')
# base64编码
base64_str=base64.b64encode(byte_info)
print(base64_str)
# base64解码
base64_str='eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogImxxeiIsICJhZG1pbiI6IHRydWV9'
str_url = base64.b64decode(base64_str).decode("utf-8")
print(str_url)

drf-jwt安装和简单使用

安装

pip install djangorestframework-jwt

使用

自定义返回内容

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

# 序列化类 继承TokenObtainPairSerialize
class AutoLoginSerializer(TokenObtainPairSerializer):
    # 重写父类的全局钩子
    def validate(self, attrs):
        dic = super().validate(attrs)
        data = {
            'code': 100,
            'message': '登陆成功',
            'username': self.user.username,
            'refresh': dic.get('refresh'),
            'access': dic.get('access')
        }
        return data

# 视图类
class AutoUserView(GenericViewSet):
    serializer_class = AutoLoginSerializer

    @action(methods=['POST'], detail=False)
    def login(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            return Response(serializer.validated_data)
        
        
# 注册路由
router.register(prefix='auto', viewset=AutoUserView, basename='auto')

# settings配置
# JWT配置 里面具体配置可以参考文档
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),  # 配置过期时间
    'REFRESH_TOKEN_LIFETIME': timedelta(days=3),
    # 用于生成access和刷refresh的序列化器。
    "TOKEN_OBTAIN_SERIALIZER": "app02.serializer.AutoLoginSerializer",
}


INSTALLED_APPS = [
    # 不要忘记注册app
    'rest_framework_simplejwt',
]

定制payload内容

​ 只需要重写get_token方法,该方法传入user对象,返回token

class AutoLoginSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        # 往第二段数据里面加东西 这里加一个名字
        token['name'] = user.username
        return token

    # 重写全局钩子
    def validate(self, attrs):
        dic = super().validate(attrs)
        data = {
            'code': 100,
            'message': '登陆成功',
            'username': self.user.username,
            'refresh': dic.get('refresh'),
            'access': dic.get('access')
        }

        return data

多方式登录

​ 可以通过多种方式登录,如用户名/手机号/邮箱

视图类

class UserView(GenericViewSet, CreateModelMixin):
    serializer_class = None

    def get_serializer_class(self):
        if self.action == 'login':
            return UserLSerializer
        else:
            return UserRSerializer

    @action(methods=['POST'], detail=False)
    def register(self, request):
        res = super().create(request)
        return Response({'code': 200, 'message': '注册成功', 'result': res.data})

    @action(methods=['POST'], detail=False)
    def login(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            refresh = serializer.context.get('refresh')
            access = serializer.context.get('access')
            return Response({'code': 200, 'message': '登录成功', 'refresh': refresh, 'access': access})
        return Response(serializer.errors)

序列化类

class UserLSerializer(serializers.Serializer):
    # 因为登录的时候只输入用户名密码
    # 所以只需要序列化两个字段
    username = serializers.CharField()
    password = serializers.CharField()

    # 这个方法用来拿到登录对象
    def _get_user(self, attrs):
        # 手机号正则表达式
        phone_regex = r'^1[3-9]\d{9}$'
        # 邮箱正则表达式
        email_regex = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
        # 拿到 手机号/邮箱/用户名
        username = attrs.get('username')
        # 拿到密码
        password = attrs.get('password')
        # 下面分别做判断 看用户名是以什么方式登录
        if re.match(phone_regex, username):
            user = User.objects.filter(mobile=username).first()
        elif re.match(email_regex, username):
            user = User.objects.filter(email=username).first()
        else:
            user = User.objects.filter(username=username).first()
        if user and user.check_password(password):
            return user


    def validate(self, attrs):
        user = self._get_user(attrs)
        # 如果上面验证失败user就为None,就抛异常
        if not user:
            raise ValidationError('用户名或密码错误')
        refresh = RefreshToken.for_user(user)
        self.context['refresh'] = str(refresh)
        self.context['access'] = str(refresh.access_token)
        return attrs

自定义用户表,手动签发和认证

# 自定义用户表,不是扩写auth的user表
# 自己写登陆签发token

视图类

class NormaUserView(GenericViewSet, CreateModelMixin):
    serializer_class = None

    def get_serializer_class(self):
        if self.action == 'login':
            return NormalUserLSerializer
        else:
            return NormalUserRSerializer

    @action(methods=['POST'], detail=False)
    def register(self, request):
        res = super().create(request)
        return Response({'code': 200, 'message': '注册成功', 'result': res.data})

    @action(methods=['POST'], detail=False)
    def login(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            refresh = serializer.context.get('refresh')
            access = serializer.context.get('access')
            return Response({'code': 200, 'message': '登录成功', 'refresh': refresh, 'access': access})
        return Response(serializer.errors)

序列化类

class NormalUserRSerializer(ModelSerializer):
    class Meta:
        model = NormalUser
        fields = ['username', 'password']

    def validate(self, attrs):
        username = attrs.get('username')
        user = NormalUser.objects.filter(username=username).first()
        if user:
            raise ValidationError(f'用户{user.username}已存在')
        return attrs


class NormalUserLSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField()

    def _get_user(self, attrs):
        # 拿到 手机号/邮箱/用户名
        username = attrs.get('username')
        # 拿到密码
        password = attrs.get('password')
        user = NormalUser.objects.filter(username=username, password=password).first()
        print(user)
        return user

    def validate(self, attrs):
        user = self._get_user(attrs)
        if not user:
            raise ValidationError('用户名或密码错误')
        token = RefreshToken.for_user(user)
        access = str(token.access_token)
        refresh = str(token)
        self.context['access'] = access
        self.context['refresh'] = refresh
        return attrs

自定义用户表,手动签发和认证

​ 现在我定义一个图书表,只有登录之后的用户才能调用这张表的接口

图书视图类 序列化类

class BookView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()
    authentication_classes = [CommonAuthentication]
    
    
class BookSerializer(ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

认证类

from rest_framework.exceptions import AuthenticationFailed

# 继承JWTAuthentication
class CommonAuthentication(JWTAuthentication):
    # 重写authenticate
    def authenticate(self, request):
        # 从请求头中取出token
        token = request.META.get('HTTP_AUTHORIZATION')
        # 如果没有token就说明没有登录,所以抛出异常
        if not token:
            raise AuthenticationFailed('请先登录再操作')
        # 调用父类get_validated_token以此校验token,返回值是payload段,包含用户id
        validated_token = self.get_validated_token(token)
        # 取到id,从而得到用户对象
        user_id = validated_token.get('user_id')
        user = NormalUser.objects.filter(pk=user_id).first()
        return user, token

标签:username,get,self,JWT,token,user,serializer
From: https://www.cnblogs.com/Hqqqq/p/18149606

相关文章

  • 使用kratos框架集成jwt认证
    问题背景:网络鉴权在web开发中最常见不过了,最近在使用gokratos框架写项目的时候需要用到jwt,查阅资料后自己动手实现了简单的鉴权,在这里记录一下实现步骤,直接上代码。定义jwt签发函数这部分我是参考了李文周老师的博客博客地址:https://www.liwenzhou.com/posts/Go/json-web-tok......
  • 08-接口文档和JWT
    接口文档楔子接口文档对于协调前后端开发非常重要,可以避免因为开发习惯不同而导致的意外情况。在项目中,如果前后端开发各自为战,可能会出现不一致的情况。因此,接口文档可以约束双方,确保他们按照统一的规范进行开发,从而提高协同开发的效率和一致性。规范接口文档一般包括以下内......
  • 异常处理、接口文档、 jwt介绍、
    【异常处理详见excel的异常处理的源码总结】#APIView--->dispatch--->三大认证,视图类的方法,如果出了异常,#会被异常捕获,捕获后统一处理#关键就是dispatch里面的response=self.handle_exception(exc)这行代码#drf内置了一个函数,只要上面过程出了异常,就会执行这个函......
  • jwt json web token
    签发----登录成功后签发认证---需要登录才能访问的接口,通过认证才能继续操作2原理-----jwt和session,cookie区别2.1cookies2.2session2.2.1sessionmiddleware2.3jwttooken长什么样?三段式,用.分割每段b64编码 1st段:公司信息,加密方式就是jwt,一般固定 2nd......
  • keycloak~jwt的rs256签名的验证方式
    接口地址keycloak开放接口地址:/auth/realms/fabao/.well-known/openid-configurationrsa算法相关术语RSA算法是一种非对称加密算法,其安全性基于大整数分解的困难性。在RSA算法中,有以下几个关键参数:n(模数):n是一个大整数,通常为两个大素数p和q的乘积,即n=p*q。n用......
  • SpringBoot+SpringSecurity6+Jwt最小化代码
    SpringBoot+SpringSecurity6+Jwt最小化代码[toc]零、参考资料https://blog.csdn.net/m0_71273766/article/details/132942056一、快速开始1.1、引入依赖<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0&quo......
  • JWT 前端笔记
    JWTJTW全称JsonWebToken没有JWT的时候,服务端给客户端发送信息,客户端返回信息,通常存在cookie或localstorage,不过服务器无法信任这个信息传给服务器的信息可能是被篡改的,也可能是被伪造的JWT的雏形原始信息和密钥一样的情况下才能生成相同签名info.签名返回后,服务器用相......
  • 02_Web Api使用Jwt
    JWT(JSONWebToken)是一种用于在网络应用之间传递信息的开放标准(RFC7519)。它使用JSON对象在安全可靠的方式下传递信息,通常用于身份验证和信息交换。在WebAPI中,JWT通常用于对用户进行身份验证和授权。当用户登录成功后,服务器会生成一个Token并返回给客户端,客户端在接下来的请求......
  • JWT的失效与续签问题。 。。。。
    jwt缺陷token一旦下发便不受服务端控制,如果发生token泄露,服务器也只能任其蹂躏,在其未过期期间不能有任何措施,同时存在以下两个问题:1、失效问题,因为token是存放在客户端的,服务端无法主动让token失效,比如踢人下线、用户权限发生变化等场景就实现不了2、续签问题,token有效期一般......
  • 【JWT】令牌-概述&&基本使用
    目录JWT令牌的使用1.需求2.是什么3.工作流程4.怎么用JWT令牌的使用1.需求​在实现系统登录功能的时候,我们需要实现用户完成一次登录之后,在后续的请求中不再需要反复登录,以前我们经常用到的技术是session即用户发出登录的请求到服务器。验证成功之后,创建sess......