Django REST framework JWT
我们在验证完用户的身份后(检验用户名和密码),需要向用户签发JWT,在需要用到用户身份信息的时候,还需核验用户的JWT。
关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。
文档网站http://getblimp.github.io/django-rest-framework-jwt/
安装配置
安装
pip install djangorestframework-jwt
配置(settings.py)
# 指定认证
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
# JWT配置
JWT_AUTH = {
# 指定有效期
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_admin.utils.jwt_response_payload_handler',
}
- JWT_EXPIRATION_DELTA 指明token的有效期
账号登录
1. 业务说明
验证用户名和密码,验证成功后,为用户签发JWT,前端将签发的JWT保存下来。
2. 后端接口设计
请求方式: POST meiduo_admin/authorizations/
请求参数: JSON 或 表单
参数名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
username | str | 是 | 用户名 |
password | str | 是 | 密码 |
返回数据: JSON
{
"username": "python",
"user_id": 1,
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5LCJ1c2VybmFtZSI6InB5dGhvbjgiLCJleHAiOjE1MjgxODI2MzQsImVtYWlsIjoiIn0.ejjVvEWxrBvbp18QIjQbL1TFE0c0ejQgizui_AROlAU"
}
返回值 | 类型 | 是否必须 | 说明 |
---|---|---|---|
username | str | 是 | 用户名 |
id | int | 是 | 用户id |
token | str | 是 | 身份认证凭据 |
3. 后端实现
新建子应用:
总路由:
url('^meiduo_admin/', include('meiduo_admin.urls')),
Django REST framework JWT提供了登录签发JWT的视图,可以直接使用
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^authorizations/$', obtain_jwt_token),
]
但是默认的返回值仅有token,我们还需在返回值中增加username和user_id。
通过修改该视图的返回值可以完成我们的需求。
在meiduo_admin/utils.py 中,新建
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
"""
return {
'token': token,
'id': user.id,
'username': user.username
}
【方法重写】
修改配置文件
# JWT配置
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_admin.utils.jwt_response.jwt_response_payload_handler',
}
4. 增加支持管理员用户登录账号
JWT扩展的登录视图,在收到用户名与密码时,也是调用Django的认证系统中提供的authenticate()来检查用户名与密码是否正确。
我们可以通过修改Django认证系统的认证后端(主要是authenticate方法)来支持登录账号既可以是用户名也可以是手机号。
修改Django认证系统的认证后端需要继承django.contrib.auth.backends.ModelBackend
,并重写authenticate方法。
authenticate(self, request, username=None, password=None, **kwargs)
方法的参数说明:
- request 本次认证的请求对象
- username 本次认证提供的用户账号
- password 本次认证提供的密码
我们想要让管理员用户才能登录我们的admin后台,这时我们就要修改django原有的用户验证方法。
重写authenticate方法的思路:
- 根据username参数查找用户User对象,在查询条件中在加上is_staff=True的条件
- 若查找到User对象,调用User对象的check_password方法检查密码是否正确
在meiduo_mall/utils/authenticate.py中编写:
【前期用命令创建管理员会默认生成is_superuser字段,在项目系统管理员中新增管理员时,创建的为is_staff字段】
from django.contrib.auth.backends import ModelBackend
import re
from users.models import User
class MeiduoModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
# 判断是否通过vue组件发送请求
if request is None:
try:
user = User.objects.get(username=username, is_staff=True)
except:
return None
# 判断密码
if user.check_password(password):
return user
else:
# 变量username的值,可以是用户名,也可以是手机号,需要判断,再查询
try:
# if re.match(r'^1[3-9]\d{9}$', username):
# user = User.objects.get(mobile=username)
# else:
# user = User.objects.get(username=username)
user = User.objects.get(username=username)
except:
# 如果未查到数据,则返回None,用于后续判断
try:
user = User.objects.get(mobile=username)
except:
return None
# return None
# 判断密码
if user.check_password(password):
return user
else:
return None
在配置文件中告知Django使用我们自定义的认证后端
前端保存token
我们可以将JWT保存在cookie中,也可以保存在浏览器的本地存储里,我们保存在浏览器本地存储中
浏览器的本地存储提供了sessionStorage 和 localStorage 两种:
- sessionStorage 浏览器关闭即失效
- localStorage 长期有效
使用方法
sessionStorage.变量名 = 变量值 // 保存数据
sessionStorage.变量名 // 读取数据
sessionStorage.clear() // 清除所有sessionStorage保存的数据
localStorage.变量名 = 变量值 // 保存数据
localStorage.变量名 // 读取数据
localStorage.clear() // 清除所有localStorage保存的数据
var vm = new Vue({
...
methods: {
...
on_submit: function(){
axios.post(...)
.then(response => {
// 记录用户的登录状态
sessionStorage.clear();
localStorage.clear();
localStorage.token = response.data.token;
localStorage.username = response.data.username;
localStorage.user_id = response.data.id;
location.href = '/index.html';
})
.catch(...)
}
}
})
postman 测试: