赞
踩
因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的头部承载两部分:声明类型,声明加密算法
- headers = {
- "type":"jwt",
- "alg":"HS256"
- }
然后将头部进行base64加密。(该加密是可以对称解密的),构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInR5cGUiOiJqd3QifQ
playload
载荷就是存放有效信息的地方,这个名字像是特指飞机上承载的货品,这些有效信息包含三部分:
标准中注册声明(建议不强制使用):
公共的声明:
私有的声明:
- {
- "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': '删除订单'})
-
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
定于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
-
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
中间件进行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']
-
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
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)
-
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
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': '删除订单'})
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
如下结果演示:
然后拿去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'
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
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("认证失败了。。。")
-
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制token
,放在AUTHORIZATION
发送带认证类接口
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。