因http协议自己为无状态,这样每次用户发出请求,咱们并不能区分是哪一个用户发出的请求,这样咱们能够经过保存cookie以便于识别是哪一个用户发来的请求,传统凡事基于session认证。可是这种认证自己不少缺陷,扩展性差,CSRF等问题。JWT(Json web token) 相比传统token,设计更为紧凑且安全。经过JWT能够实现用户认证等操做。python
pyJWT下载web
pip install pyJWT
JWT构成:算法
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInR5cGUiOiJqd3QifQ.eyJ1c2VybmFtZSI6InhqayIsImV4cCI6MTU4MjU0MjAxN30.oHdfcsUftJJob66e5mL1jLRpJwiG0i9MOD5gzM476eY
jwt是由三段信息构成,将3部分信息构成JWT字符串,经过点进行分割,第一部分称为头部(header),第二部分称为在和(payload相似于飞机上承载的物品),第三部分是签证(signature)。数据库
headerdjango
jwt的头部承载两部分:声明类型,声明加密算法api
headers = { "type":"jwt", "alg":"HS256" }
而后将头部进行base64加密。(该加密是能够对称解密的),构成了第一部分安全
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInR5cGUiOiJqd3QifQ
playloadrestful
载荷就是存放有效信息的地方,这个名字像是特指飞机上承载的货品,这些有效信息包含三部分:cookie
标准中注册声明(建议不强制使用):session
公共的声明:
私有的声明:
{ "username": "xjk", }
signature
views视图:
from django.http import JsonResponse from django.views import View from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from utils.jwt_auth import create_token # 定义method_decorator 免 csrf校验, dispatch表示全部请求,由于全部请求都先通过dispatch @method_decorator(csrf_exempt,name="dispatch") class LoginView(View): """ 登录校验 """ def post(self,request,*args,**kwargs): user = request.POST.get("username") pwd = request.POST.get("password") # 这里简单写一个帐号密码 if user == "xjk" and pwd == "123": # 登录成功进行校验 token = create_token({"username":"xjk"}) # 返回JWT token return JsonResponse({"status":True,"token":token}) return JsonResponse({"status":False,"error":"用户名密码错误"}) # 定义method_decorator 免 csrf校验, dispatch表示全部请求,由于全部请求都先通过dispatch @method_decorator(csrf_exempt,name="dispatch") class OrderView(View): """ 登录后能够访问 """ def get(self, request, *args, **kwargs): # 打印用户jwt信息 print(request.user_info) return JsonResponse({'data': '订单列表'}) def post(self, request, *args, **kwargs): print(request.user_info) return JsonResponse({'data': '添加订单'}) def put(self, request, *args, **kwargs): print(request.user_info) return JsonResponse({'data': '修改订单'}) def delete(self, request, *args, **kwargs): print(request.user_info) return JsonResponse({'data': '删除订单'})
定于jwt工具 utils/jwt_auth.py
import jwt import datetime from jwt import exceptions # 加的盐 JWT_SALT = "ds()udsjo@jlsdosjf)wjd_#(#)$" def create_token(payload,timeout=20): # 声明类型,声明加密算法 headers = { "type":"jwt", "alg":"HS256" } # 设置过时时间 payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=20) result = jwt.encode(payload=payload,key=JWT_SALT,algorithm="HS256",headers=headers).decode("utf-8") # 返回加密结果 return result def parse_payload(token): """ 用于解密 :param token: :return: """ result = {"status":False,"data":None,"error":None} try: # 进行解密 verified_payload = jwt.decode(token,JWT_SALT,True) result["status"] = True result['data']=verified_payload except exceptions.ExpiredSignatureError: result['error'] = 'token已失效' except jwt.DecodeError: result['error'] = 'token认证失败' except jwt.InvalidTokenError: result['error'] = '非法的token' return result
中间件进行jwt校验 middlewares/jwt.py
class JwtAuthorizationMiddleware(MiddlewareMixin): """ 用户须要经过请求头的方式来进行传输token,例如: Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU """ def process_request(self, request): # 若是是登陆页面,则经过 if request.path_info == '/login/': return # 非登陆页面须要校验token authorization = request.META.get('HTTP_AUTHORIZATION', '') print(authorization) auth = authorization.split() # 验证头信息的token信息是否合法 if not auth: return JsonResponse({'error': '未获取到Authorization请求头', 'status': False}) if auth[0].lower() != 'jwt': return JsonResponse({'error': 'Authorization请求头中认证方式错误', 'status': False}) if len(auth) == 1: return JsonResponse({'error': "非法Authorization请求头", 'status': False}) elif len(auth) > 2: return JsonResponse({'error': "非法Authorization请求头", 'status': False}) token = auth[1] # 解密 result = parse_payload(token) if not result['status']: return JsonResponse(result) # 将解密后数据赋值给user_info request.user_info = result['data']
settings注册中间件
MIDDLEWARE = [ ... 'middlewares.jwt.JwtAuthorizationMiddleware', ... ]
以下结果演示:
setting.py 要引入restframework
INSTALLED_APPS = [ 'rest_framework', ]
认证类定义:
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.authentication import BaseAuthentication from rest_framework import exceptions from utils.jwt_auth import parse_payload class JwtQueryParamAuthentication(BaseAuthentication): """ 用户须要在url中经过参数进行传输token,例如: http://www.pythonav.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU """ def authenticate(self, request): # 从url上获取jwt token token = request.query_params.get('token') payload = parse_payload(token) if not payload['status']: raise exceptions.AuthenticationFailed(payload) # 若是想要request.user等于用户对象,此处能够根据payload去数据库中获取用户对象。 return (payload, token) class JwtAuthorizationAuthentication(BaseAuthentication): """ 用户须要经过请求头的方式来进行传输token,例如: Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU """ def authenticate(self, request): # 非登陆页面须要校验token,从头信息拿去JWT Token authorization = request.META.get('HTTP_AUTHORIZATION', '') auth = authorization.split() if not auth: raise exceptions.AuthenticationFailed({'error': '未获取到Authorization请求头', 'status': False}) if auth[0].lower() != 'jwt': raise exceptions.AuthenticationFailed({'error': 'Authorization请求头中认证方式错误', 'status': False}) if len(auth) == 1: raise exceptions.AuthenticationFailed({'error': "非法Authorization请求头", 'status': False}) elif len(auth) > 2: raise exceptions.AuthenticationFailed({'error': "非法Authorization请求头", 'status': False}) token = auth[1] result = parse_payload(token) if not result['status']: raise exceptions.AuthenticationFailed(result) # 若是想要request.user等于用户对象,此处能够根据payload去数据库中获取用户对象。 return (result, token)
view.py使用
from rest_framework.views import APIView from rest_framework.response import Response from utils.jwt_auth import create_token from extensions.auth import JwtQueryParamAuthentication, JwtAuthorizationAuthentication class LoginView(APIView): def post(self, request, *args, **kwargs): """ 用户登陆 """ user = request.POST.get('username') pwd = request.POST.get('password') # 检测用户和密码是否正确,此处能够在数据进行校验。 if user == 'xjk' and pwd == '123': # 用户名和密码正确,给用户生成token并返回 token = create_token({'username': 'xjk'}) return Response({'status': True, 'token': token}) return Response({'status': False, 'error': '用户名或密码错误'}) class OrderView(APIView): # 经过url传递token authentication_classes = [JwtQueryParamAuthentication, ] # 经过Authorization请求头传递token # authentication_classes = [JwtAuthorizationAuthentication, ] def get(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '订单列表'}) def post(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '添加订单'}) def put(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '修改订单'}) def delete(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '删除订单'})
以下结果演示:
而后拿去JWT Token 添加到url上,发送给其余路由请求。
rest_framework_jwt是封装jwt符合restful规范接口
安装:
pip install djangorestframework-jwt
演示前必须作一些操做
settings.py配置
INSTALLED_APPS = [ ... 'rest_framework' ] import datetime #超时时间 JWT_AUTHTIME = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # token前缀 'JWT_AUTH_HEADER_PREFIX': 'JWT', } # 引用Django自带的User表,继承使用时须要设置 AUTH_USER_MODEL = 'api.User'
models.py创建表
from django.db import models # Create your models here. from django.contrib.auth.models import AbstractUser class User(AbstractUser): CHOICE_GENDER = ( (1,"男"), (2,"女"), (3,"不详"), ) gender = models.IntegerField(choices=CHOICE_GENDER,null=True,blank=True) class Meta: db_table = "user"
定义一个路由建立一个用户
urlpatterns = [ url(r'^reg/', views.RegView.as_view()), ]
建立注册用户视图:
class RegView(APIView): def post(self,request,*args,**kwargs): receive = request.data username = receive.get("username") password = receive.get("password") user = User.objects.create_user( username=username, password=password ) user.save() return Response({"code":200,"msg":"ok"})
/reg
发送post
请求建立用户开始使用jwt
在url添加登录路由
from django.conf.urls import url from django.contrib import admin from rest_framework_jwt.views import obtain_jwt_token from api import views urlpatterns = [ # 登入验证,使用JWT的模块,只要用户密码正确会自动生成一个token返回 url(r'^login/', obtain_jwt_token), # 访问带认证接口 url(r'^home/', views.Home.as_view()), ]
访问login/
:
定义认证视图:
class Home(APIView): authentication_classes = [JwtAuthorizationAuthentication] def get(self,request,*args,**kwargs): return Response({"code":200,"msg":"this is home"})
定义认证类JwtAuthorizationAuthentication
:
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer class JwtAuthorizationAuthentication(BaseAuthentication): def authenticate(self, request): # 获取头信息token authorization = request.META.get('HTTP_AUTHORIZATION', '') print(authorization) # 校验 valid_data = VerifyJSONWebTokenSerializer().validate({"token":authorization}) """ valid_data = {'token': '太长了省略一下...' 'user': <User: xjk> } """ user = valid_data.get("user") if user: return else: raise AuthenticationFailed("认证失败了。。。")
复制token
,放在AUTHORIZATION
发送带认证类接口