一捞奕、寫(xiě)在前面
這兩天重寫(xiě)了之前項(xiàng)目前臺(tái)與后臺(tái)對(duì)接的代碼稚虎。引入 Rest 框架和 samplejwt。在重寫(xiě)過(guò)程中參考網(wǎng)上文章發(fā)現(xiàn)很多文章多是 django 1.11和 jwt的文章坑鱼。所以紀(jì)錄分享
django 2.2 + samplejwt 與 vue-element-admin對(duì)接的方法如下持灰。
調(diào)試通過(guò)后的代碼分享在github,大家有興趣的可自行下載怎燥。
注:當(dāng)前僅調(diào)試登錄驗(yàn)證框架瘫筐,logout功能后續(xù)再補(bǔ)充。
后臺(tái)代碼地址:https://github.com/Sirius1942/banana/releases/tag/V0.0.1
前臺(tái)代碼地址:https://github.com/Sirius1942/rock/releases/tag/V0.0.1
代碼主要參考官方說(shuō)明:上面demo中很多參考官方示例代碼铐姚,有疑惑地方大家可自行查找
django rest framework 官方文檔:https://www.django-rest-framework.org/
django 官方文檔:擴(kuò)展和自定義后端https://docs.djangoproject.com/en/2.2/topics/auth/customizing/
django-rest-framework-simplejwt 官方代碼說(shuō)明:https://github.com/davesque/django-rest-framework-simplejwt (建議看代碼和官方說(shuō)明策肝,網(wǎng)上資料比較少)
django-cros 官方代碼及說(shuō)明:https://github.com/ottoyiu/django-cors-headers
vue-element-admin 官方指南:https://panjiachen.github.io/vue-element-admin-site/zh/guide/
以下主要參考的下面網(wǎng)上文章,感謝相關(guān)作者的支持
二隐绵、主要修改部分及代碼實(shí)現(xiàn)過(guò)程
1之众、創(chuàng)建django-admin項(xiàng)目,新建user App 這里不詳細(xì)說(shuō)明依许,請(qǐng)參考相關(guān)文檔棺禾。
2、擴(kuò)展user 模型峭跳,滿(mǎn)足vue 需要:
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
#userProfile繼承AbstractUser分類(lèi)帘睦,進(jìn)行拓展
class UserProfile(AbstractUser):
"""
用戶(hù)類(lèi)拓展
"""
# 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
在設(shè)置文檔(settings.py)中 添加默認(rèn)類(lèi)
# 添加AUTH_USRE_MODEL 替換默認(rèn)的user
AUTH_USER_MODEL = 'user.UserProfile'
別忘檢查下是否添加了userapp
INSTALLED_APPS = [
'user.apps.UserConfig',
]
下面補(bǔ)充 login方法,正常情況下你應(yīng)該已經(jīng)完成用戶(hù)模型的字段的補(bǔ)充坦康,并且集成好了rest framework 和jwt 這時(shí)登錄
上面的圖片顯示已經(jīng)使用了 restframework 且jwt生效所以 直接訪問(wèn)users 想獲取用戶(hù)列表 由于沒(méi)有權(quán)限不能查看到用戶(hù)信息。
下面自定義登錄的 serializer 以便可以補(bǔ)充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)
# ... 官方示例中上面的部分沒(méi)有生效
# 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
注意滞欠,安官方說(shuō)明是不需要 validate 方法的。不過(guò)查看了 samplejwt的源代碼肆良,發(fā)現(xiàn)丟與token的封裝都是在這個(gè)方法中進(jìn)行的筛璧。所以按下面的方式封裝后成功。
以下是官方代碼截圖 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
補(bǔ)充封裝后的View方法
#別忘了from引用剛剛新增的 serializer
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
修改url 調(diào)用
urlpatterns = [
...
path('user/login', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
....
]
完成以上應(yīng)該可以通過(guò)正確的 用戶(hù)名和密碼獲取到 token
使用postman 測(cè)試如下:
下面新增userinfo 接口
目前簡(jiǎn)單實(shí)現(xiàn)惹恃,直接加了一個(gè)接口夭谤。在 user views 中補(bǔ)充 get_user_info 方法
def get_user_info(request):
User = get_user_model()
if request.method=='GET':
print("in get")
# print(dir(request))
#獲取請(qǐng)求參數(shù)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)
注:這里遇到個(gè)問(wèn)題是 request 方法中沒(méi)有 django 傳統(tǒng)的 META 方法,所以直接使用 JWTAuthentication.authenticate 方法會(huì)報(bào)錯(cuò)巫糙。這里手工調(diào)用 get_user方法朗儒,通過(guò) token獲取對(duì)應(yīng)的user對(duì)象。
另外: data 后續(xù)可以通過(guò)序列化優(yōu)化,這里先留一個(gè)坑后面填
繼續(xù)添加 user info url
path('user/info',views.get_user_info),
以上登錄后通過(guò)token自動(dòng)獲取用戶(hù)信息后臺(tái)封裝完畢
下面來(lái)繼續(xù)修改前臺(tái)
前臺(tái)vue 代碼中主要需要修改 utils/request.js
補(bǔ)充消息頭:config.headers.Authorization = getToken()
config.headers['Content-Type'] = 'application/json'
// request interceptor
service.interceptors.request.use(
config => {
// 后臺(tái)使用jwt時(shí)候發(fā)送post請(qǐng)求必須使用 formdata模式
if (config.method === 'post') {
// JSON 轉(zhuǎn)換為 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 時(shí)修改 從 .access對(duì)象獲取 修改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) // 對(duì)接jwt token改成access 獲取token
setToken(data.access)
resolve()
}).catch(error => {
reject(error)
})
})
},
其余由于后臺(tái)的role目前采用1個(gè)字段所以前臺(tái)獲取user地方臨時(shí)處理成醉锄,如果傳入的roles不是list 將其轉(zhuǎn)化成list 乏悄。注意下面代碼當(dāng)獲取不到role時(shí)還是會(huì)報(bào)錯(cuò),只要role不為空不會(huì)有問(wèn)題恳不,留個(gè)坑后面填
// 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)
}
以上基本上是全部修改檩小,可能有記不清的地方,大家如果執(zhí)行代碼時(shí)候有問(wèn)題歡迎留言烟勋。
后續(xù)繼續(xù)填的坑规求,再慢慢更新:
1、logout —— auth與jwt卵惦、rest框架整合阻肿。
2、user與groups 關(guān)聯(lián)
3鸵荠、多roles 權(quán)限更改與判斷
4冕茅、前臺(tái)完整的用戶(hù)管理