zoukankan      html  css  js  c++  java
  • jwt原理+应用

    前言

    Josn Web Token简称jwt,是目前最流行的跨域用户身份验证解决方案,jwt相较于传统的token认证,它的的优势在于服务端无需对用户的token进行保存,而是使用算法完成token的生成和校验。

    基于传统的token认证

    1.用户登录  服务端放回给客户端1个token,并将token保存在服务端

    2.用户再次访问时 需要携带token,服务端获取token后,再去数据库进行校验。

    """
    Django settings for jwt_demo project.
    
    Generated by 'django-admin startproject' using Django 1.11.4.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/1.11/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/1.11/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = '2o=cm-!m%j**0&@9bgjq(zj!@ifw$5^o(4w@psst65l$1=2vmf'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = []
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'rest_framework'#加载rest_framework
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'jwt_demo.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR,  'templates'),],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'jwt_demo.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/1.11/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    
    STATIC_URL = '/static/'
    settings.py
    """jwt_demo URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/1.11/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.conf.urls import url, include
        2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
    """
    from django.conf.urls import url
    from django.contrib import admin
    from api import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/login/$', views.Login_View.as_view()),
        url(r'^api/order/$', views.Order_View.as_view()),
    ]
    urls.py
    from django.shortcuts import render
    import  uuid
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from api import models
    class Login_View(APIView):
        '''用户登录 '''
        def post(self,request,*args,**kwargs):
            print(21222)
            user=request.data.get('username')
            pwd = request.data.get('password')
            user_object=models.UserInfo.objects.filter(username=user).first()
            if not user_object:
                return Response({'code':1000,'error':'用户名/密码错误'})
    
            random_string = str(uuid.uuid4())
            user_object.token=random_string
            user_object.save()
            return Response({'code': 1001, 'data': random_string})
    
    
    
    
    class Order_View(APIView):
        def get(self, request, *args, **kwargs):
            token=request.query_params.get('token')
            if not token:
                return Response({'code':2000,'error':'登录成功之后才能访问'})
            user_obj=models.UserInfo.objects.filter(token=token).first()
            if not user_obj:
                return Response({'code': 2000, 'error':'token'})
            return Response('订单列表')
    views.py

    jwt工作流程

    1.如果用户登录成功,服务端使用jwt创建1个token,并返回用户。

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.   
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
    SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    web token特征

    由3段字符串组成,并且使用  . 连接起来。

    web token生成过程

    第一段 HEADER

    内部保存算法和token类型

    让该json转换成字符串,然后进行base64url 加密然后把加密后的字符串+替换为_。(base64算法可以反解)

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

    生成:
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
     

    第二段PAYLOAD:

    用户自定义的值

    让该json转换成字符串,然后进行base64url 加密然后把加密后的字符串+替换为_。(base64算法可以反解)

    {
      "id": "1234567890",
      "name": "zhanggen",
      "iat": 1516239022  #超时时间
    }

    第三段:VERIFY SIGNATURE

    a.对第1、2部分密文进行拼接。

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

    b.对第1、2部分密文进行hash256加密,并加盐。

    c.对hash256加密之后的密码,再次进行base256加密。

    2.用户再次访问服务端,需要携带token 服务端对token进行校验。

    a.获取token

    b.通过.对web token进行切割,划分为3段

    c.对第二段进行base64url解密获取 payload信息,检测web token是否超时

    {
      "id": "1234567890",
      "name": "zhanggen",
      "iat": 1516239022  #超时时间
    }

    d.然后通base64url加密解密后的 第二段数据,把第1、2段密文进行拼接

    e.对第1、2部分密文  进行hash256加密,并加盐。再次得到第3段数据

    f.让新生成的第3段 和从用户那里分割出来的第3段, 进行密文对比。检查 web token是否有效或者中途被修改过?

    g.最后通过验证

    3.总结

    web token的核心加密算法就是把token分3段,前2段可以解密,第3段不可以解密。第3段 = 前2段的拼接(hash256加密+加盐)生成。

    在这里我们在后端进行加密、解密用到的盐是至关重要的。

     

     

    jwt应用

    jwt已经通过 第三方包的方式集成到Python。使用非常简单。

    1.安装pyjwt模块

    D:jwt_demo>pip install pyjwt -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
    Collecting pyjwt
      Downloading http://pypi.doubanio.com/packages/87/8b/6a9f14b5f781697e51259d81657e6048fd31a113229cf346880bb7545565/PyJWT-1.7.1-py2.py3-none-any.whl
    Installing collected packages: pyjwt
    Successfully installed pyjwt-1.7.1
    
    D:jwt_demo>

    2.基于Django 的 DRF使用

    from django.shortcuts import render
    
    import  uuid
    import datetime
    from jwt import exceptions as jwt_exceptions
    import jwt
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from api import models
    
    
    
    class Login_View(APIView):
        '''用户登录 '''
        def post(self,request,*args,**kwargs):
            print(21222)
            user=request.data.get('username')
            pwd = request.data.get('password')
            user_object=models.UserInfo.objects.filter(username=user).first()
            if not user_object:
                return Response({'code':1000,'error':'用户名/密码错误'})
    
            random_string = str(uuid.uuid4())
            user_object.token=random_string
            user_object.save()
            return Response({'code': 1001, 'data': random_string})
    
    class Order_View(APIView):
        def get(self, request, *args, **kwargs):
            token=request.query_params.get('token')
            if not token:
                return Response({'code':2000,'error':'登录成功之后才能访问'})
            user_obj=models.UserInfo.objects.filter(token=token).first()
            if not user_obj:
                return Response({'code': 2000, 'error':'token'})
            return Response('订单列表')
    
    
    
    import  uuid
    import datetime
    from jwt import exceptions as jwt_exceptions
    import jwt
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from api import models
    salt = 'dsfhkjhiejgnvjcxhwwwwwwwwwww'
    class JwtLogin_View(APIView):
        '''基于Jwt用户登录 '''
        def post(self,request,*args,**kwargs):
            user=request.data.get('username')
            pwd = request.data.get('password')
            user_object=models.UserInfo.objects.filter(username=user).first()
            if not user_object:
                return Response({'code':1000,'error':'用户名/密码错误'})
    
    
            #构造header头部
            headers={
                    "typ": "JWT",
                    "alg": "HS256",
                    }
            #构造payload
            payload={
                  "user_id": user_object.pk,
                  "user_name": user_object.username,
                  "exp": datetime.datetime.utcnow() +datetime.timedelta(minutes=1)  #超时时间1分钟
                }
            #生成 web token  key=要加的盐 一定要保密啊!!
            web_token=jwt.encode(headers=headers,payload=payload,algorithm='HS256',key=salt).decode('utf-8')
            return Response({'code': 1001, 'data': web_token})
    
    class JwtOrder_View(APIView):
        def get(self, request, *args, **kwargs):
            #获取token
            token=request.query_params.get('token')
            verified_payload=None
            msg=None
            try:
                # 解析token,得到第3段,True等于校验
                #注意啦!!加密、解密用得都是同1个盐!!!!千万不能泄露
                verified_payload=jwt.decode(token,salt,True)##
            except jwt_exceptions.ExpiredSignature:
                msg='Token已经超时'
            except jwt.DecodeError:
                msg='Token认证失败'
            except jwt.InvalidTokenError:
                msg='非法的Token'
            if not verified_payload:
                return Response({'code':1003,'error':msg})
            #获取第二段 用户自定义的信息
            print(verified_payload['user_id'],verified_payload['user_name'])
            return Response('订单列表')
  • 相关阅读:
    蛤圈不能圈一切---逐梦蛤蛤圈
    Unix培训基础
    java培训基础
    数据库基础
    Python生成requirements.txt方法
    免费高质量代理IP
    lxml库的xpath的使用
    python自带GUI库tkinter教程,全网最全最好
    selenium博客
    Linux-Centos下selenium部署
  • 原文地址:https://www.cnblogs.com/sss4/p/12334313.html
Copyright © 2011-2022 走看看