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('订单列表')
原文地址:https://www.cnblogs.com/sss4/p/12334313.html