Django rest+jwt+vue-element-admi
一、写在前面
这两天重写了之前项目前台与后台对接的代码。引入 Rest 框架和 samplejwt。在重写过程中参考网上文章发现很多文章多是 django 1.11和 jwt的文章。所以纪录分享
django 2.2 + samplejwt 与 vue-element-admin对接的方法如下。
调试通过后的代码分享在github,大家有兴趣的可自行下载。
注:当前仅调试登录验证框架,logout功能后续再补充。
后台代码地址:https://github.com/Sirius1942/banana/releases/tag/V0.0.1
前台代码地址:https://github.com/Sirius1942/rock/releases/tag/V0.0.1
代码主要参考官方说明:上面demo中很多参考官方示例代码,有疑惑地方大家可自行查找
-
django rest framework 官方文档:https://www.django-rest-framework.org/
-
django 官方文档:扩展和自定义后端https://docs.djangoproject.com/en/2.2/topics/auth/customizing/
-
django-rest-framework-simplejwt 官方代码说明:https://github.com/davesque/django-rest-framework-simplejwt (建议看代码和官方说明,网上资料比较少)
-
django-cros 官方代码及说明:https://github.com/ottoyiu/django-cors-headers
-
vue-element-admin 官方指南:https://panjiachen.github.io/vue-element-admin-site/zh/guide/
以下主要参考的下面网上文章,感谢相关作者的支持
二、主要修改部分及代码实现过程
1、创建django-admin项目,新建user App 这里不详细说明,请参考相关文档。
2、扩展user 模型,满足vue 需要:
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
#userProfile继承AbstractUser分类,进行拓展
class UserProfile(AbstractUser):
"""
用户类拓展
"""
# name = models.CharField(max_length=30, null=True, blank=True, verbose_name="姓名" )
avatar = models.CharField(max_length=100, null=True, blank=True, verbose_name="avatar")
role = models.CharField(max_length=10, default="editor", verbose_name="role")
introduction = models.TextField(max_length=500,null=True,blank=True, verbose_name="introduction")
class Meta:
verbose_name = "user"
verbose_name_plural = verbose_name
def __str__(self):
return self.username
在设置文档(settings.py)中 添加默认类
# 添加AUTH_USRE_MODEL 替换默认的user
AUTH_USER_MODEL = 'user.UserProfile'
别忘检查下是否添加了userapp
INSTALLED_APPS = [
'user.apps.UserConfig',
]
下面补充 login方法,正常情况下你应该已经完成用户模型的字段的补充,并且集成好了rest framework 和jwt 这时登录
image.png上面的图片显示已经使用了 restframework 且jwt生效所以 直接访问users 想获取用户列表 由于没有权限不能查看到用户信息。
下面自定义登录的 serializer 以便可以补充vue所需要的code,message 字段
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
print('in MyTokenObtainPairSerializer')
token = super().get_token(user)
# Add custom claims
#token['username'] = user.username
#token['code'] = 20000
# print(token)
# ... 官方示例中上面的部分没有生效
# print(token)
return token
def validate(self,attrs):
data = super().validate(attrs)
re_data={}
re_data['data']=data
re_data['code']=20000
re_data['message']='success'
return re_data
注意,安官方说明是不需要 validate 方法的。不过查看了 samplejwt的源代码,发现丢与token的封装都是在这个方法中进行的。所以按下面的方式封装后成功。
以下是官方代码截图 get_token 方法仅返回加密的token而已
class TokenObtainSlidingSerializer(TokenObtainSerializer):
@classmethod
def get_token(cls, user):
return SlidingToken.for_user(user)
def validate(self, attrs):
data = super().validate(attrs)
token = self.get_token(self.user)
data['token'] = str(token)
return data
补充封装后的View方法
#别忘了from引用刚刚新增的 serializer
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
修改url 调用
urlpatterns = [
...
path('user/login', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
....
]
完成以上应该可以通过正确的 用户名和密码获取到 token
使用postman 测试如下:
image.png
下面新增userinfo 接口
目前简单实现,直接加了一个接口。在 user views 中补充 get_user_info 方法
def get_user_info(request):
User = get_user_model()
if request.method=='GET':
print("in get")
# print(dir(request))
#获取请求参数token的值
token=request.headers.get('AUTHORIZATION')
# test=request.META.get('CONTENT-TYPE')
# print(test)
# print(token)
token_msg=authentication.JWTAuthentication().get_validated_token(token)
print(token_msg)
user_object=authentication.JWTAuthentication().get_user(token_msg)
data = {"username":user_object.username,
"first_name":user_object.first_name,
"last_name": user_object.last_name,
"avatar":user_object.avatar,
# "groups":user_object.groups,
"roles":user_object.role,
"introduction":user_object.introduction
}
re_data={"data": data,
"code": 20000,
"message": "success"
}
return JsonResponse(re_data)
注:这里遇到个问题是 request 方法中没有 django 传统的 META 方法,所以直接使用 JWTAuthentication.authenticate 方法会报错。这里手工调用 get_user方法,通过 token获取对应的user对象。
另外: data 后续可以通过序列化优化,这里先留一个坑后面填
继续添加 user info url
path('user/info',views.get_user_info),
以上登录后通过token自动获取用户信息后台封装完毕
下面来继续修改前台
前台vue 代码中主要需要修改 utils/request.js
补充消息头:config.headers.Authorization = getToken()
config.headers['Content-Type'] = 'application/json'
// request interceptor
service.interceptors.request.use(
config => {
// 后台使用jwt时候发送post请求必须使用 formdata模式
if (config.method === 'post') {
// JSON 转换为 FormData
const formData = new FormData()
Object.keys(config.data).forEach(key => formData.append(key, config.data[key]))
config.data = formData
}
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
// config.headers['X-Token'] = getToken()
config.headers.Authorization = getToken()
}
config.headers['Content-Type'] = 'application/json'
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
另外需要在获取token 时修改 从 .access对象获取 修改store/modules/user.js 中 set_token 中 data.access
const actions = {
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { data } = response
console.log('in store')
commit('SET_TOKEN', data.access) // 对接jwt token改成access 获取token
setToken(data.access)
resolve()
}).catch(error => {
reject(error)
})
})
},
其余由于后台的role目前采用1个字段所以前台获取user地方临时处理成,如果传入的roles不是list 将其转化成list 。注意下面代码当获取不到role时还是会报错,只要role不为空不会有问题,留个坑后面填
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
// reject('getInfo: roles must be a non-null array!')
var roles_list = []
roles_list[0] = roles
commit('SET_ROLES', roles_list)
} else {
commit('SET_ROLES', roles)
}
以上基本上是全部修改,可能有记不清的地方,大家如果执行代码时候有问题欢迎留言。
后续继续填的坑,再慢慢更新:
1、logout —— auth与jwt、rest框架整合。
2、user与groups 关联
3、多roles 权限更改与判断
4、前台完整的用户管理