django-jwt
一.认识
认识:
- 浏览器访问服务端,服务器根据自己保存的secret_key生成token,返回给浏览器(这个secret_key默认使用settings.py中的secret_key)
- 浏览器再次访问服务器带着后台返回的token,服务器根据secret_key对token前两段内容进行加密生成第三段内容和前端第三段内容进行比较,相同就可以,再一个就是取出前端的第二段内容进行过期验证
1.jwt构成
jwt就是一段字符串,三段信息构成,如下:
u23o1u4hfkadhglahwbkjfsdjlsgjaljg.fsdjkjrlj4l2j4l2j3lk4jlfnasjgbajskhgahghi2q42o134.ou4o24j2o424j2ofow
1.header
jwt的头部有两部分信息(此处不要存储敏感信息):
- 声明类型
- 声明算法,通常使用HMAC SHA256
完整头部如下:
{
'typ': 'JWT', #csrf也是一种类型,这里是固定的
'alt':'HS256' #生成签证时使用
}
将头部进行base64.bs4endcode()加密(该加密是对称的,可解密),构成第一部分
u23o1u4hfkadhglahwbkjfsdjlsgjaljg
2.payload
载荷就是存放有效信息的地方:
- 标准中注册的声明
- 公共声明
- 私有声明
标准中注册的声明(建议但不强制使用):
- iss:jwt签发者
- sub:jwt所面向的用户
- aud:接收jwt的一方
- exp:jwt的过期时间,这个过期时间必须大于签发时间
- iat:jwt的签发时间
- jti:jwt的唯一身份标识,用来作为一次性token,从而回避攻击
公共的声明:公共声明可添加任何信息
定义一个payload,json格式的数据
{
"sub":"1234123412",
"exp":"4214124", #时间戳格式
"name":"John Doe",
}
3.signature
JWT的第三部分是一个签证信息,由三部分构成(这部分用来校验token有效性):
- header(base64之后的)
- payload(base64之后的)
- secret密钥
这部分需要base64加密后的header和payload,然后加上secret组合加密,构成了JWT的第三部分
二.drf应用
1.安装
pip install djangorestframework-jwt
2.配置
import datetime
# drf框架的配置信息
REST_FRAMEWORK = {
# 设置所有接口都需要被验证
'DEFAULT_PERMISSION_CLASSES': (
#’rest_framework.permissions.IsAuthenticatedOrReadOnly’,
),
# 用户登陆认证方式
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
# jwt载荷中的有效期设置
JWT_AUTH = {
#token 有效期
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=8),
#是否验证token有效期
'JWT_ALLOW_REFRESH': True,
#续期有效期(该设置可在24小时内带未失效的token 进行续期)
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),
# 自定义返回格式,需要手工创建,默认的只会返回token
'JWT_RESPONSE_PAYLOAD_HANDLER': ‘Users.utils.jwt_response_payload_handler’,
}
AUTH_USER_MODEL = "users.UserInfo"
#JWT_EXPIRATION_DELTA和JWT_REFRESH_EXPIRATION_DELTA:在django中,如果token在设置的8hour之内进行token刷新,是没什么问题的,但一旦过了8hour,再刷新token也没用,此时就需要前端跳到登录界面重新登录,因此JWT_REFRESH_EXPIRATION_DELTA并没有多大用处
自定义返回格式(在app(Users)下新建utils.py)
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
"""
return {
'token': token,
'id': user.id,
'username': user.username
}
models.py
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
name = models.CharField(max_length=20)
tel = models.CharField(max_length=20)
#创建完成后
#1.python manage.py makemigrations && migrate
#2.python manage.py createsuperuser会在UserInfo表中生成一条记录,因为setting.py中指定了AUTH_USER_MODEL
urls.py
from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token,refresh_jwt_token,verify_jwt_token
urlpatterns = [
url(r'^login/',obtain_jwt_token),
url(r'^verify/',verify_jwt_token), #校验token有效性
url(r'^refresh/',refresh_jwt_token) #刷新并返回新的token
]
#前端发来post的json请求,须指定键值对{"username":"xx","password":"xx"},obtain_jwt_token会默认从AUTH_USER_MODEL对应的表中查询数据,如果正确匹配,则返回token值,如果不正确,返回400错误
#refresh_jwt_token是验证并刷新token,如果发来的token有效,则生成新的token并返回给前端,否则返回400错误。
login.vue:登录界面,登录成功后获取token并存入sessionstorage|localstorage
axios({
method: "post",
url: this.$setting.HTTP + "users/login/",
headers: {
"Content-Type": "multipart/form-data"
},
withCredentials: true,
data: formdata
})
.then(res => {
// console.log(res.data.token);
sessionStorage.setItem("token", res.data.token);
this.$router.push("/base/showCenter");
})
.catch(function(error) {
// this.$message.error('错了哦,这是一条错误消息');
});
index.vue:是登录后跳转的界面,每次进入这个界面都要判断是否有token以及token是否校验成功,如果没有token,返回登录界面,如果有,再发送到后台去验证
created(){
let thiss = this;
let token = sessionStorage.getItem("token");
//判断本地是否存在token
if (token){
//如果存在token,还需要去后台校验token是否有效
axios({
method:"post",
//超过JWT_EXPIRATION_DELTA设置的时间后返回400错误
url: this.$setting.HTTP + "users/refresh/",
data:{"token":token},
}).then((res)=>{
console.log(res);
//sessionStorage.setItem("token",res.data.token)
}).catch(function (error) {
thiss.$router.push("/")
})
}else {
thiss.$router.push('/login')
}
3.drf中验证
以上没有用到drf,只是默认的前后端交互的一些配置。当我们访问某个接口时,需要在headers带上token,后端验证
from django.shortcuts import render
from rest_framework.generics import ListAPIView
from host import models,utils
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
# Create your views here.
class ListView(ListAPIView):
authentication_classes = [JSONWebTokenAuthentication,] //验证前端header中携带的token
queryset = models.Host.objects.filter(id=1)
serializer_class = utils.HostSerializer
postman中headers中携带固定键值对{"Authorization":"JWT xxxxxxxx"},注意JWT和token以空格隔开