安装模块 jwt是一个第三方模块
pip install djangorestframework-jwt -i https://pypi.douban.com/simple/
JWT的使用 (一) 自定义返回的
settings.py 里面配置一下
REST_FRAMEWORK = {
# 引入JWT认证机制,当客户端将jwt token传递给服务器之后
# 此认证机制会自动校验jwt token的有效性,无效会直接返回401(未认证错误)
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
'PAGE_SIZE': 100, # 显示多少数据
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 防止跳出 警告
}
# JWT扩展配置
JWT_AUTH = {
# 重新定义jwt认证成功后返回的数据 days(天) hours(时) minutes(分)
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 定义token过期时间1天
}
手动生成 JWT token
该方法 在后面会用!!!
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
子应用---模型类---views.py
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
"""自定义用户模型类"""
mobile = models.CharField(max_length=11, unique=True, verbose_name='Mobile')
class Meta:
db_table = 'tb_users'
verbose_name = '用户'
verbose_name_plural = verbose_name
def __str__(self):
return self.username
子应用里的 urls.py
# coding:utf-8
from django.conf.urls import url
from apps.user.views import *
app_name = 'user'
urlpatterns = [
url(r'^register/$', RegisterView.as_view()), # 用户注册接口
url(r'^mobiles/$', MobileCountView.as_view()), # 校验手机号码是否注册过
url(r'^login/$', AdminAuthorizeView.as_view()), # 用户jwt认证接口
]
views.py
# coding:utf-8
import re
from django import http
from django.contrib.auth import authenticate, login
from django.views import View
from django_redis import get_redis_connection
from pymysql import DatabaseError
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.settings import api_settings
from apps.user.models import User
from utils.response_code import RET
class AdminAuthorizeView(APIView):
def post(self, request):
"""
登录
:param request:
:return:
"""
username = request.POST.get('username')
password = request.POST.get('password')
if username is None or password is None:
return Response({'code': 500, 'message': '请求参数错误'})
is_login = authenticate(request, username=username, password=password)
if is_login is None:
return Response({'code': 500, 'message': '账号或密码错误'})
login(request, is_login) # 保存登录状态 但没必要 因为已经是 jwt认证了
### 此处为生成 token 的地方 ###
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(is_login)
token = jwt_encode_handler(payload)
return Response({'code': RET.OK, 'message': "登录成功", "token": token})
class RegisterView(APIView):
def post(self, request):
"""
注册
:param request:
:return:
"""
password = request.POST.get('password')
mobile = request.POST.get('mobile')
# 短信验证码
# sms_code = request.POST.get('sms_code')
# 判断参数是否齐全
if not all([password, mobile]):
return http.HttpResponseBadRequest('缺少必传参数')
# 判断密码是否是8-20个数字
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
return http.HttpResponseBadRequest('请输入8-20位的密码')
# 判断手机号是否合法
if not re.match(r'^1[3-9]\d{9}$', mobile):
return http.HttpResponseBadRequest('请输入正确的手机号码')
# redis_conn = get_redis_connection('code')
# sms_code_saved = redis_conn.get('sms_%s' % mobile)
# if sms_code_saved is None:
# return Response({'code': RET.NODATA, 'errmsg': '无效的验证码'})
# if sms_code != sms_code_saved.decode():
# return Response({'code': RET.DATAERR, 'errmsg': '验证码输入错误'})
try:
User.objects.create_user(username=mobile, password=password, mobile=mobile)
except DatabaseError:
return Response({'code': RET.DBERR, 'message': '注册失败'})
return Response({'code': RET.OK, 'message': "完成注册"})
class MobileCountView(View):
def get(self, request):
"""
校验手机号码是否重复注册
:param request:
:return:
"""
mobile = request.GET.get('mobile')
count = User.objects.filter(mobile=mobile).count()
return http.JsonResponse({'code': RET.OK, 'count': count})
前端返回token后,后端校验token
安装cors
pip install django-cors-headers
前后端分离 会有跨域的问题 并且 没有使用到 COOKIES SESSIONS
settings.py文件里面 配置跨域
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'apps.user', # user
'corsheaders', # 跨域
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 跨域
'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',
]
# 跨域增加忽略
CORS_ALLOW_CREDENTIALS = True # 允许携带cookie
CORS_ORIGIN_ALLOW_ALL = True
# 设置允许的请求
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
'VIEW',
)
# 设置允许的header
CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'Pragma',
)
自定义基于jwt的权限类 (第一种方法)
新建一个auth.py 用于认证用户是否登录 在一些需要登录后才能访问的视图做校验
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework.exceptions import AuthenticationFailed
import jwt
from apps.user.models import User
class MyBaseAuthentication(BaseAuthentication):
# 重写authenticate
def authenticate(self, request):
# 获取token的第二部分
jwt_value = request.META.get('HTTP_AUTHORIZATION')
# 注意此处为 postman 的时候请求 会带有 Bearer 所以使用了切割字符串
jwt_value = jwt_value.split()[1] # 浏览器请求头上传的时候 可以将此句注释掉
if not jwt_value:
# 如果没有就抛异常
raise AuthenticationFailed('您没有携带认证信息')
try:
# 将token反解成用户信息字典
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('认证超时')
except jwt.InvalidTokenError:
raise AuthenticationFailed('非法用户')
except Exception as e:
raise AuthenticationFailed(str(e))
# 直接返回对象 不查库,速度快些(只能获取传入的参数的值)
# user = User(id=payload.get('user_id'), username=payload.get('username'))
# 去数据库中查找获取user对象(能获取的字段更多)
user = User.objects.filter(pk=payload.get('user_id')).first()
return user, jwt_value
自定义基于jwt的权限类 (第二种方法)
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework.exceptions import AuthenticationFailed
import jwt
# 继承BaseJSONWebTokenAuthentication,重写父类的authenticate方法
class MyToken(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
# 获取token的第二部分
jwt_value = request.META.get('HTTP_AUTHORIZATION')
print(jwt_value)
# 判断get请求头中是否携带token,如果没有就是游客模式,直接返回None
if jwt_value is None:
return None
try:
# 将token数据进行decode解码后得到payload(用户信息字典),如果token超时或者错误,就会抛出异常
payload = jwt_decode_handler(jwt_value)
# token = jwt_encode_handler(payload) 获取token
except jwt.ExpiredSignature:
raise AuthenticationFailed('认证超时')
except jwt.InvalidTokenError:
raise AuthenticationFailed('非法用户')
except Exception as e:
raise AuthenticationFailed(str(e))
# 将用户信息字典传入,内部会查询数据库返回user对象
user = self.authenticate_credentials(payload)
# 最后返回两个参数,一个是user对象,另一个是token(也可以是其他数据看你自己要返回啥)
return user, jwt_value
使用POSTman 测试
class SearchCateGoryView(APIView):
# authentication_classes = [MyToken, ]
### 自定义认证 JWT 方法
authentication_classes = [MyBaseAuthentication, ]
def get(self,request):
data = 'get方法'
return Response({"code":"OK",'message':data})