六十一、jwt认证

一 cookie,session和token介绍

https://www.cnblogs.com/liuqingzheng/p/8990027.html
cookie:是存储在客户端的键值对。
session:是存储在服务端的键值对。
token:是三段式,由服务端生成,返回给给客户端(浏览器存储在cookie中,移动端存储在硬盘中),服务端不存储。

django中的session认证机制
在这里插入图片描述

二 jwt原理介绍

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

"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inh1eGlhb3h1IiwiZXhwIjoxNjY1NTYxNjk0LCJlbWFpbCI6IjNAcXEuY29tIn0.
BtO6Ls2vSdrsJHkjZzkzmJ71Ue6zEIBkHV_iEmoZfEI"

第一部分是头部(header),第二部分是荷载(payload),第三部分是签名(signature)。

2.1 header

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

头部包含两部分信息,typ是类型,alg是加密算法。
然后将头部进行base64编码(该编码是可以对称解码的),构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

2.2 payload

{
      "exp": "1234567890",
      "name": "John Doe",
      "userid": 3
}

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。

对其进行编码得到

eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6Imphc3BlciIsImV4cCI6MTY2NTU2Mjc0MywiZW1haWwiOiIyQHFxLmNvbSJ9

2.3 signature

把编码后的header和编码后的payload使用.连接起来,加密后得到的结果。

BtO6Ls2vSdrsJHkjZzkzmJ71Ue6zEIBkHV_iEmoZfEI

django中token机制
在这里插入图片描述

三 base64编码与解码

在jwt、网络中传输字符串、网络中传输图片都可以使用base64编码,base64可以反解。

import base64
import json


header = json.dumps({
    'typ': 'JWT',
    'alg': 'HS256'
})

# 编码
res = base64.b64encode(header.encode(encoding='utf8'))
print(res)  # b'eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9'

# 解码
res1 = base64.b64decode(res)
print(res1)  # b'{"typ": "JWT", "alg": "HS256"}'
print(res1.decode(encoding='utf8'))  # {"typ": "JWT", "alg": "HS256"}

四 rest_framework_jwt快速使用

  1. 安装
    pip3 install djangorestframework-jwt
    
  2. 基于auth_user表签发token,将数据库迁移。
  3. 创建用户。
  4. 登录接口已经写好了,只用带上参数发送post请求就可以签发了。
  5. 配置路由
    from rest_framework_jwt.views import obtain_jwt_token
    
    urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', obtain_jwt_token),
    ]
    
  6. 访问http://127.0.0.1:8000/login/
    在这里插入图片描述
  7. 认证:在视图类中配置认证类和权限类
    from rest_framework.views import APIView
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.response import Response
    
    
    class UserView(APIView):
        authentication_classes = [JSONWebTokenAuthentication]
        permission_classes = [IsAuthenticated]
    
        def get(self, request):
            return Response('ok')
    
    在这里插入图片描述
    请求头中必须携带Authorization:jwt token串

五 jwt自定义返回数据格式

自定义一个函数
utils.py

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

配置配置文件
settings.py

JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER':
        'app01.utils.jwt_response_payload_handler',
}

在这里插入图片描述

六 自定义user表,签发token

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import User
from rest_framework.exceptions import ValidationError

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class UserView(APIView):

    def post(self, request):
        try:
            username = request.data.get('username')
            password = request.data.get('password')
            user = User.objects.get(username=username, password=password)
            if user:
                payload = jwt_payload_handler(user)
                token = jwt_encode_handler(payload)
                print(payload, token)
                return Response({
                    'code': 100,
                    'msg': '登录成功',
                    'user': payload.get('username'),
                    'token': token}
                )
        except Exception as e:
            raise ValidationError(e)

七 自定义认证类,验证token

utils.py

class MyJSONWebTokenAuthentication(JSONWebTokenAuthentication):
    def authenticate(self, request):
        jwt_value = self.get_jwt_value(request)
        if jwt_value is None:
            return None

        try:
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            msg = _('Signature has expired.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = _('Error decoding signature.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()

        user = self.authenticate_credentials(payload)

        return (user, jwt_value)

views.py

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .models import User
from rest_framework.exceptions import ValidationError
from .utils import MyJSONWebTokenAuthentication

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class UserView(APIView):

    def post(self, request):
        try:
            username = request.data.get('username')
            password = request.data.get('password')
            user = User.objects.get(username=username, password=password)
            if user:
                payload = jwt_payload_handler(user)
                token = jwt_encode_handler(payload)
                print(payload, token)
                return Response({
                    'code': 100,
                    'msg': '登录成功',
                    'user': payload.get('username'),
                    'token': token}
                )
        except Exception as e:
            raise ValidationError(e)


class User2View(APIView):
    authentication_classes = [MyJSONWebTokenAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return Response('ok')

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.UserView.as_view()),
    path('user/', views.User2View.as_view()),
]

八 自定义签发token,校验token

views.py

from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework.views import APIView
from drf_day10 import settings
from .utils import MyJSONWebTokenAuthentication
from .models import User

import time
import json
import base64
import hashlib


class UserView(APIView):
    def post(self, request):
        try:
            username = request.data.get('username')
            password = request.data.get('password')
            user = User.objects.get(username=username, password=password)
        except Exception as e:
            raise APIException(e)
        user_id = user.id
        username = user.username
        exp = int(time.time() + 300)
        header = base64.b64encode(json.dumps({"typ": "JWT", "alg": "md5"}).encode(encoding='utf8'))
        payload = base64.b64encode(
            json.dumps({'user_id': user_id, 'username': username, 'exp': exp}).encode(encoding='utf8'))
        signature = header + b'.' + payload
        md5 = hashlib.md5()
        md5.update(signature)
        # SECRET是在配置文件中配置的密钥(盐)
        md5.update(settings.SECRET.encode(encoding='utf8'))
        token = (header + b'.' + payload).decode(encoding='utf8') + '.' + md5.hexdigest()
        return Response({
            'code': 100,
            'msg': '登录成功',
            'user': json.loads(base64.b64decode(payload)).get('username'),
            'token': token}
        )


class User2View(APIView):
    authentication_classes = [MyJSONWebTokenAuthentication]

    def get(self, request):
        return Response('ok')

utils.py

from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from drf_day10 import settings
from .models import User

import base64
import json
import time
import hashlib


class MyJSONWebTokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        try:
            jwt_value = request.META.get('HTTP_AUTHORIZATION', b'')
            left, right = jwt_value.split(' ')
            header, payload, signature = right.split('.')
            header = json.loads(base64.b64decode(header.encode(encoding='utf8')))
            payload = json.loads(base64.b64decode(payload.encode(encoding='utf8')))
            exe = payload.get('exp')
        except:
            raise AuthenticationFailed('错误解码签名')
        if exe < int(time.time()):
            raise AuthenticationFailed('令牌已过期')
        re_signature = base64.b64encode(json.dumps(header).encode(encoding='utf8')) + b'.' + base64.b64encode(
            json.dumps(payload).encode(encoding='utf8'))
        md5 = hashlib.md5()
        md5.update(re_signature)
        md5.update(settings.SECRET.encode(encoding='utf8'))
        re_signature = md5.hexdigest()
        if signature != re_signature:
            raise AuthenticationFailed('令牌校验失败')
        user = User.objects.filter(id=payload.get('user_id')).first()
        return user, None

urls.py

urlpatterns = [
    path('login/', views.UserView.as_view()),
    path('user/', views.User2View.as_view()),
]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

补充知识

请求头中:X_FORWARDED_FOR 是什么?

1 没有使用代理服务器的情况:

REMOTE_ADDR = 您的 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示

2 使用透明代理服务器的情况:Transparent Proxies

REMOTE_ADDR = 最后一个代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 您的真实 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

这类代理服务器还是将您的信息转发给您的访问对象,无法达到隐藏真实身份的目的。

3 使用普通匿名代理服务器的情况:Anonymous Proxies

REMOTE_ADDR = 最后一个代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 代理服务器 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

隐藏了您的真实IP,但是向访问对象透露了您是使用代理服务器访问他们的。

4 使用欺骗性代理服务器的情况:Distorting Proxies

REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 随机的 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

告诉了访问对象您使用了代理服务器,但编造了一个虚假的随机IP代替您的真实IP欺骗它。

5 使用高匿名代理服务器的情况:High Anonymity Proxies (Elite proxies)

REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示 ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

完全用代理服务器的信息替代了您的所有信息,就象您就是完全使用那台代理服务器直接访问对象。

6 集群和分布式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值