zoukankan      html  css  js  c++  java
  • jwt认证

    jwt认证

    1、工作原理

    '''
    1.jwt = base64(头部).base(载荷).hash256(base64(头部).base(载荷).密钥)
    2.base64是可逆的算法、hash是不可逆的算法
    3.密钥是固定的字符串,保存在服务器
    '''
    

    2、drf-jwt

    官网

    http://getblimp.github.io/django-rest-framework-jwt/
    

    安装:虚拟环境

    pip install djangorestframework-jwt
    

    使用:user/urls.py

    from django.urls import path
    from rest_framework_jwt.views import obtain_jwt_token
    urlpatterns = [
        path('login/', obtain_jwt_token),
    ]
    

    测试接口:post请求

    """
    postman发生post请求
    
    接口:http://api.luffy.cn:8000/user/login/
    
    数据:
    {
        "username":"admin",
        "password":"admin"
    }
    """
    

    3、drf-jwt开发

    默认配置信息:JWT_AUTH到dev.py中

    import datetime
    JWT_AUTH = {
        # 过期时间
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
        # 自定义认证结果:见下方序列化user和自定义response
        'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',  
    }
    

    自定义配置信息:在项目的setting中配置

    # drf-jwt自定义配置
    import datetime
    JWT_AUTH = {
        # 过期时间(days=)也有秒等时间单位
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
        # 是否允许刷新,(这里是否允许刷新是是否是每登录一次刷新一下登录时间)
        'JWT_ALLOW_REFRESH': True,
        # 最大刷新的过期时间(从开始登录加上刷新刷新不得超过days=)
        'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
    }
    

    自定义response:user/utils.py

    from .serializers import UserModelSerializers
    def jwt_response_payload_handler(token, user=None, request=None):
        return {
            'status': 0,
            'msg': 'ok',
            'data': {
                'token': token,
                'user': UserModelSerializers(user).data
            }
        }
    

    基于drf-jwt的全局认证:user/authentications.py(自己创建)

    import jwt
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework_jwt.authentication import jwt_decode_handler
    from rest_framework_jwt.authentication import get_authorization_header
    from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
    
    class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            jwt_value = get_authorization_header(request)
    
            if not jwt_value:
                raise AuthenticationFailed('Authorization 字段是必须的')
            try:
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('签名过期')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('非法用户')
            user = self.authenticate_credentials(payload)
    
            return user, jwt_value
    

    全局启用:settings/dev.py

    REST_FRAMEWORK = {
        # 认证模块
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'user.authentications.JSONWebTokenAuthentication',
        ),
    }
    

    局部启用、禁用:任何一个cbv类首行

    # 局部禁用
    authentication_classes = []
    
    # 局部启用
    from user.authentications import JSONWebTokenAuthentication
    authentication_classes = [JSONWebTokenAuthentication]
    

    多方式登录:user/utils.py

    import re
    from .models import User
    from django.contrib.auth.backends import ModelBackend
    class JWTModelBackend(ModelBackend):
        def authenticate(self, request, username=None, password=None, **kwargs):
            try:
                if re.match(r'^1[3-9]d{9}$', username):
                    user = User.objects.get(mobile=username)
                else:
                    user = User.objects.get(username=username)
            except User.DoesNotExist:
                return None
            if user.check_password(password) and self.user_can_authenticate(user):
                return user
    

    配置多方登录:settings/dev.py

    AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend']
    

    手动签发JWT:了解-可以拥有原生登录基于Model类user对象签发JWT

    from rest_framework_jwt.settings import api_settings
    
    jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
    jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    
    payload = jwt_payload_handler(user)
    token = jwt_encode_handler(payload)
    

    jwt认证规则总结

    """
    jwt: json web token
    优点:
    1)数据库不需要存储token,所以服务器的 IO 操作会减少(没有IO写操作)
    2)客户端存Token,服务器只存储签发与校验算法,执行效率高
    3)签发与校验算法在多个服务器上可以直接统一,所以jwt认证规则下,服务器做集群非常便捷
    
    突破点:
    1)token必须要有多个部分组成,有能反解的部分,也要有不能反解的部分 - jwt采用的都是三段式
    2)token中必须包含过期时间,保证token的安全性与时效性
    
    jwt原理:
    1)jwt由 头.载荷.签名 三部分组成
    2)每一部分数据都是一个json字典,头和载荷采用 base64 可逆加密算法加密,签名采用 HS256 不可逆加密
    
    内容:
    1)头(基本信息):可逆不可逆采用的加密算法、公司名称、项目组信息、开发者信息...
    {
    	"company": "小女孩",
    	...
    }
    2)载荷(核心信息):用户主键、用户账号、客户端设备信息、过期时间...
    {
    	'pk': 1,
    	...
    }
    3)签名(安全信息):头的加密结果、载荷的加密结果、服务器的安全码(盐)...
    {
    	"header": "..."
    	...
    }
    
    签发算法:
    1)头内容写死(可以为空{}):公司、项目组信息都是固定不变的
    	=> 将数据字典转化成json字符串,再将json字符串加密成base64字符串
    	
    2)载荷的内容:用户账号、客户端设备信息是由客户端提供,用户主键是客户端提供账号密码校验User表通过后才能确定,过期时间根据当前时间与配置的过期时间相结合产生
    	=> 将数据字典转化成json字符串,再将json字符串加密成base64字符串
    	
    3)签名的内容,先将头的加密结果,载荷的加密结果作为成员,再从服务器上拿安全码(不能让任何客户端知道),也可以额外包含载荷的部分(用户信息,设备信息)
    	=> 将数据字典转化成json字符串,再将json字符串不可逆加密成HS256字符串
    	
    4)将三个字符串用 . 连接产生三段式token
    
    校验算法:
    1)从客户端提交的请求中拿到token,用 . 分割成三段(如果不是三段,非法)
    2)头(第一段)可以不用解密
    3)载荷(第二段)一定需要解密,先base64解密成json字符串,再转换成json字典数据
    	i)用户主键与用户账号查询User表确定用户是否存在
    	ii)设备信息用本次请求提交的设备信息比对,确定前后是否是同一设备,决定是否对用户做安全提示(eg:短信邮箱提示异地登录)(同样的安全保障还可以为IP、登录地点等)
    	iii)过期时间与当前时间比对,该token是否在有效时间内
    4)签名(第三段)采用加密碰撞校验
    	i)将头、载荷加密字符串和数据库安全码形成json字典,转换成json字符串
    	ii)采用不可逆HS256加密形成加密字符串
    	iii)新的加密字符串与第三段签名碰撞比对,一致才能确保token是合法的
    	
    5)前方算法都通过后,载荷校验得到的User对象,就是该token代表的登录用户(Django项目一般都会把登录用户存放在request.user中)
    
    
    刷新算法:
    1)要在签发token的载荷中,额外添加两个时间信息:第一次签发token的时间,最多往后刷新的有效时间
    2)每一请求携带token,不仅走校验算法验证token是否合法,还要额外请求刷新token的接口,完成token的刷新:校验规则与校验算法差不多,但是要将过期时间后移(没有超过有效时间,产生新token给客户端,如果超过了,刷新失败)
    3)所以服务器不仅要配置过期时间,还需要配置最长刷新时间
    """
    
  • 相关阅读:
    Why Choose Jetty?
    Jetty 的工作原理以及与 Tomcat 的比较
    Tomcat设计模式
    Servlet 工作原理解析
    Tomcat 系统架构
    spring boot 打包方式 spring boot 整合mybaits REST services
    wireshark udp 序列号 User Datagram Protocol UDP
    Maven 的聚合(多模块)和 Parent 继承
    缓存策略 半自动化就是mybaitis只支持数据库查出的数据映射到pojo类上,而实体到数据库的映射需要自己编写sql语句实现,相较于hibernate这种完全自动化的框架我更喜欢mybatis
    Mybatis解决sql中like通配符模糊匹配 构造方法覆盖 mybits 增删改
  • 原文地址:https://www.cnblogs.com/mqhpy/p/12128834.html
Copyright © 2011-2022 走看看