当前位置:   article > 正文

django中使用JWT_django jwt

django jwt

1.pyJWT简述

  • http协议本身为无状态,这样每次用户发出请求,我们并不能区分是哪个用户发出的请求,这样我们可以通过保存cookie以便于识别是哪个用户发来的请求,传统凡事基于session认证。但是这种认证本身很多缺陷,扩展性差,CSRF等问题。JWT(Json web token) 相比传统token,设计更为紧凑且安全。通过JWT可以实现用户认证等操作。

  • pyJWT下载

    pip install pyJWT
    
  • JWT构成:

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInR5cGUiOiJqd3QifQ.eyJ1c2VybmFtZSI6InhqayIsImV4cCI6MTU4MjU0MjAxN30.oHdfcsUftJJob66e5mL1jLRpJwiG0i9MOD5gzM476eY
    

    jwt是由三段信息构成,将3部分信息构成JWT字符串,通过点进行分割,第一部分称为头部(header),第二部分称为在和(payload类似于飞机上承载的物品),第三部分是签证(signature)。

  • header

    • jwt的头部承载两部分:声明类型,声明加密算法

      1. headers = {
      2. "type":"jwt",
      3. "alg":"HS256"
      4. }
    • 然后将头部进行base64加密。(该加密是可以对称解密的),构成了第一部分

      eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInR5cGUiOiJqd3QifQ
      
  • playload

    • 载荷就是存放有效信息的地方,这个名字像是特指飞机上承载的货品,这些有效信息包含三部分:

      • 标准中注册声明(建议不强制使用):

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

        • 可以添加任何信息,一般添加用户相关信息。但不建议添加敏感信息,因为该部分在客户端可解密
      • 私有的声明:

        • 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
        1. {
        2. "username": "xjk",
        3. }
  • signature

    • jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
      • header(base64后的)
      • payload(base64后的)
      • secret
    • 这个部分需要base64加密后的header和base64加密后pyload使用,连接组成的祖父穿,然后通过header中声明加密方式,进行加盐secret组合加密,这样构成第三部分

2.pyJWT在django应用

  • views视图:

    1. from django.http import JsonResponse
    2. from django.views import View
    3. from django.views.decorators.csrf import csrf_exempt
    4. from django.utils.decorators import method_decorator
    5. from utils.jwt_auth import create_token
    6. # 定义method_decorator 免 csrf校验, dispatch表示所有请求,因为所有请求都先经过dispatch
    7. @method_decorator(csrf_exempt,name="dispatch")
    8. class LoginView(View):
    9. """
    10. 登陆校验
    11. """
    12. def post(self,request,*args,**kwargs):
    13. user = request.POST.get("username")
    14. pwd = request.POST.get("password")
    15. # 这里简单写一个账号密码
    16. if user == "xjk" and pwd == "123":
    17. # 登陆成功进行校验
    18. token = create_token({"username":"xjk"})
    19. # 返回JWT token
    20. return JsonResponse({"status":True,"token":token})
    21. return JsonResponse({"status":False,"error":"用户名密码错误"})
    22. # 定义method_decorator 免 csrf校验, dispatch表示所有请求,因为所有请求都先经过dispatch
    23. @method_decorator(csrf_exempt,name="dispatch")
    24. class OrderView(View):
    25. """
    26. 登陆后可以访问
    27. """
    28. def get(self, request, *args, **kwargs):
    29. # 打印用户jwt信息
    30. print(request.user_info)
    31. return JsonResponse({'data': '订单列表'})
    32. def post(self, request, *args, **kwargs):
    33. print(request.user_info)
    34. return JsonResponse({'data': '添加订单'})
    35. def put(self, request, *args, **kwargs):
    36. print(request.user_info)
    37. return JsonResponse({'data': '修改订单'})
    38. def delete(self, request, *args, **kwargs):
    39. print(request.user_info)
    40. return JsonResponse({'data': '删除订单'})
  • 定于jwt工具 utils/jwt_auth.py

    1. import jwt
    2. import datetime
    3. from jwt import exceptions
    4. # 加的盐
    5. JWT_SALT = "ds()udsjo@jlsdosjf)wjd_#(#)$"
    6. def create_token(payload,timeout=20):
    7. # 声明类型,声明加密算法
    8. headers = {
    9. "type":"jwt",
    10. "alg":"HS256"
    11. }
    12. # 设置过期时间
    13. payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=20)
    14. result = jwt.encode(payload=payload,key=JWT_SALT,algorithm="HS256",headers=headers).decode("utf-8")
    15. # 返回加密结果
    16. return result
    17. def parse_payload(token):
    18. """
    19. 用于解密
    20. :param token:
    21. :return:
    22. """
    23. result = {"status":False,"data":None,"error":None}
    24. try:
    25. # 进行解密
    26. verified_payload = jwt.decode(token,JWT_SALT,True)
    27. result["status"] = True
    28. result['data']=verified_payload
    29. except exceptions.ExpiredSignatureError:
    30. result['error'] = 'token已失效'
    31. except jwt.DecodeError:
    32. result['error'] = 'token认证失败'
    33. except jwt.InvalidTokenError:
    34. result['error'] = '非法的token'
    35. return result
  • 中间件进行jwt校验 middlewares/jwt.py

    1. class JwtAuthorizationMiddleware(MiddlewareMixin):
    2. """
    3. 用户需要通过请求头的方式来进行传输token,例如:
    4. Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
    5. """
    6. def process_request(self, request):
    7. # 如果是登录页面,则通过
    8. if request.path_info == '/login/':
    9. return
    10. # 非登录页面需要校验token
    11. authorization = request.META.get('HTTP_AUTHORIZATION', '')
    12. print(authorization)
    13. auth = authorization.split()
    14. # 验证头信息的token信息是否合法
    15. if not auth:
    16. return JsonResponse({'error': '未获取到Authorization请求头', 'status': False})
    17. if auth[0].lower() != 'jwt':
    18. return JsonResponse({'error': 'Authorization请求头中认证方式错误', 'status': False})
    19. if len(auth) == 1:
    20. return JsonResponse({'error': "非法Authorization请求头", 'status': False})
    21. elif len(auth) > 2:
    22. return JsonResponse({'error': "非法Authorization请求头", 'status': False})
    23. token = auth[1]
    24. # 解密
    25. result = parse_payload(token)
    26. if not result['status']:
    27. return JsonResponse(result)
    28. # 将解密后数据赋值给user_info
    29. request.user_info = result['data']
  • settings注册中间件

    1. MIDDLEWARE = [
    2. ...
    3. 'middlewares.jwt.JwtAuthorizationMiddleware',
    4. ...
    5. ]
  • 如下结果演示:

    • 首先登陆获取JWT Token

    • 然后拿去JWT Token 添加到 Authorization ,发送给其他路由请求。

3.restframework与pyJWT结合

  • setting.py 要引入restframework

    1. INSTALLED_APPS = [
    2. 'rest_framework',
    3. ]
  • 认证类定义:

    1. #!/usr/bin/env python
    2. # -*- coding:utf-8 -*-
    3. from rest_framework.authentication import BaseAuthentication
    4. from rest_framework import exceptions
    5. from utils.jwt_auth import parse_payload
    6. class JwtQueryParamAuthentication(BaseAuthentication):
    7. """
    8. 用户需要在url中通过参数进行传输token,例如:
    9. http://www.pythonav.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
    10. """
    11. def authenticate(self, request):
    12. # 从url上获取jwt token
    13. token = request.query_params.get('token')
    14. payload = parse_payload(token)
    15. if not payload['status']:
    16. raise exceptions.AuthenticationFailed(payload)
    17. # 如果想要request.user等于用户对象,此处可以根据payload去数据库中获取用户对象。
    18. return (payload, token)
    19. class JwtAuthorizationAuthentication(BaseAuthentication):
    20. """
    21. 用户需要通过请求头的方式来进行传输token,例如:
    22. Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
    23. """
    24. def authenticate(self, request):
    25. # 非登录页面需要校验token,从头信息拿去JWT Token
    26. authorization = request.META.get('HTTP_AUTHORIZATION', '')
    27. auth = authorization.split()
    28. if not auth:
    29. raise exceptions.AuthenticationFailed({'error': '未获取到Authorization请求头', 'status': False})
    30. if auth[0].lower() != 'jwt':
    31. raise exceptions.AuthenticationFailed({'error': 'Authorization请求头中认证方式错误', 'status': False})
    32. if len(auth) == 1:
    33. raise exceptions.AuthenticationFailed({'error': "非法Authorization请求头", 'status': False})
    34. elif len(auth) > 2:
    35. raise exceptions.AuthenticationFailed({'error': "非法Authorization请求头", 'status': False})
    36. token = auth[1]
    37. result = parse_payload(token)
    38. if not result['status']:
    39. raise exceptions.AuthenticationFailed(result)
    40. # 如果想要request.user等于用户对象,此处可以根据payload去数据库中获取用户对象。
    41. return (result, token)
  • view.py使用

    1. from rest_framework.views import APIView
    2. from rest_framework.response import Response
    3. from utils.jwt_auth import create_token
    4. from extensions.auth import JwtQueryParamAuthentication, JwtAuthorizationAuthentication
    5. class LoginView(APIView):
    6. def post(self, request, *args, **kwargs):
    7. """ 用户登录 """
    8. user = request.POST.get('username')
    9. pwd = request.POST.get('password')
    10. # 检测用户和密码是否正确,此处可以在数据进行校验。
    11. if user == 'xjk' and pwd == '123':
    12. # 用户名和密码正确,给用户生成token并返回
    13. token = create_token({'username': 'xjk'})
    14. return Response({'status': True, 'token': token})
    15. return Response({'status': False, 'error': '用户名或密码错误'})
    16. class OrderView(APIView):
    17. # 通过url传递token
    18. authentication_classes = [JwtQueryParamAuthentication, ]
    19. # 通过Authorization请求头传递token
    20. # authentication_classes = [JwtAuthorizationAuthentication, ]
    21. def get(self, request, *args, **kwargs):
    22. print(request.user, request.auth)
    23. return Response({'data': '订单列表'})
    24. def post(self, request, *args, **kwargs):
    25. print(request.user, request.auth)
    26. return Response({'data': '添加订单'})
    27. def put(self, request, *args, **kwargs):
    28. print(request.user, request.auth)
    29. return Response({'data': '修改订单'})
    30. def delete(self, request, *args, **kwargs):
    31. print(request.user, request.auth)
    32. return Response({'data': '删除订单'})
  • 如下结果演示:

    • 首先登陆获取JWT Token

  • 然后拿去JWT Token 添加到url上,发送给其他路由请求。

4. djangorestframework-jwt

  • rest_framework_jwt是封装jwt符合restful规范接口

  • 安装:

    pip install djangorestframework-jwt
    

演示前必须做一些操作

  • settings.py配置

    1. INSTALLED_APPS = [
    2. ...
    3. 'rest_framework'
    4. ]
    5. import datetime
    6. #超时时间
    7. JWT_AUTHTIME = {
    8. 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    9. # token前缀
    10. 'JWT_AUTH_HEADER_PREFIX': 'JWT',
    11. }
    12. # 引用Django自带的User表,继承使用时需要设置
    13. AUTH_USER_MODEL = 'api.User'
  • models.py建立表

    1. from django.db import models
    2. # Create your models here.
    3. from django.contrib.auth.models import AbstractUser
    4. class User(AbstractUser):
    5. CHOICE_GENDER = (
    6. (1,"男"),
    7. (2,"女"),
    8. (3,"不详"),
    9. )
    10. gender = models.IntegerField(choices=CHOICE_GENDER,null=True,blank=True)
    11. class Meta:
    12. db_table = "user"
  • 定义一个路由创建一个用户

    1. urlpatterns = [
    2. url(r'^reg/', views.RegView.as_view()),
    3. ]
  • 创建注册用户视图:

  1. class RegView(APIView):
  2. def post(self,request,*args,**kwargs):
  3. receive = request.data
  4. username = receive.get("username")
  5. password = receive.get("password")
  6. user = User.objects.create_user(
  7. username=username, password=password
  8. )
  9. user.save()
  10. return Response({"code":200,"msg":"ok"})
  • 访问/reg 发送post请求创建用户

开始使用jwt

  • 在url添加登陆路由

    1. from django.conf.urls import url
    2. from django.contrib import admin
    3. from rest_framework_jwt.views import obtain_jwt_token
    4. from api import views
    5. urlpatterns = [
    6. # 登入验证,使用JWT的模块,只要用户密码正确会自动生成一个token返回
    7. url(r'^login/', obtain_jwt_token),
    8. # 访问带认证接口
    9. url(r'^home/', views.Home.as_view()),
    10. ]
  • 访问login/ :

  • 定义认证视图:

    1. class Home(APIView):
    2. authentication_classes = [JwtAuthorizationAuthentication]
    3. def get(self,request,*args,**kwargs):
    4. return Response({"code":200,"msg":"this is home"})
  • 定义认证类JwtAuthorizationAuthentication:

    1. from rest_framework.authentication import BaseAuthentication
    2. from rest_framework.exceptions import AuthenticationFailed
    3. from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
    4. class JwtAuthorizationAuthentication(BaseAuthentication):
    5. def authenticate(self, request):
    6. # 获取头信息token
    7. authorization = request.META.get('HTTP_AUTHORIZATION', '')
    8. print(authorization)
    9. # 校验
    10. valid_data = VerifyJSONWebTokenSerializer().validate({"token":authorization})
    11. """
    12. valid_data = {'token': '太长了省略一下...'
    13. 'user': <User: xjk>
    14. }
    15. """
    16. user = valid_data.get("user")
    17. if user:
    18. return
    19. else:
    20. raise AuthenticationFailed("认证失败了。。。")
  • 复制token,放在AUTHORIZATION 发送带认证类接口

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/893700
推荐阅读
相关标签
  

闽ICP备14008679号