第一步 在登入類中生成 jwt
class Login(MyException,APIView): def post(self,request): user_obj = models.UserInfo.objects.filter(**request.data).first() if not user_obj: return Response({"code":1001, "msg":"使用者名稱密碼錯誤"}) # 生成jwt token並返回 headers = { 'typ': 'jwt', 'alg': 'HS256' } # 構造payload payload = { 'user_id': user_obj.id, # 自定義使用者ID 'username': user_obj.username, # 自定義使用者名稱 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超時時間 } token = jwt.encode(payload=payload, key=settings.SECRET_KEY, algorithm="HS256", headers=headers) print("t:",token) return Response({"code":0, "msg":"登入成功", "token":token,"username":user_obj.username,"userid":user_obj.id})
第二步 在認證類中校驗 jwt合法性
class TestAuthentication(BaseAuthentication): def authenticate(self, request): token = request.query_params.get("token") if not token: raise AuthenticationFailed({"code":1002,"msg":"必須攜帶token"}) # 2.合法性 try: # print(token) verified_payload = jwt.decode(token, settings.SECRET_KEY, algorithms="HS256") print("v:",verified_payload) current_timestamp = int(datetime.datetime.now().timestamp()) print(current_timestamp) print(verified_payload.get("exp")) #判斷是否需要更新jwt verified_payload["exp_time"] = verified_payload.get("exp") - current_timestamp # 獲取當前時間 vs 有效期 return (verified_payload, token) except Exception as e: print(e) raise AuthenticationFailed("認證失敗")
第三步 寫一個更新jwt的類
class UpdateTokenView(ResView,APIView): authentication_classes = [MyAuthentication] def get(self, request): print("info:",request.user, request.auth) # 生成jwt token並返回 headers = { 'typ': 'jwt', 'alg': 'HS256' } # 構造payload payload = { 'user_id': request.user['user_id'], # 自定義使用者ID 'username': request.user['username'], # 自定義使用者名稱 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超時時間 } token = jwt.encode(payload=payload, key=settings.SECRET_KEY, algorithm="HS256", headers=headers) return Response(token)
第四步 在統一返回格式處,新增過期欄位
class ResView: def finalize_response(self, request, response, *args, **kwargs): response = super().finalize_response(request, response, *args, **kwargs) if response.exception: return response response.data = {"code": 0, "data": response.data,"exp_time": request.user.get("exp_time")} return response
第五步 新增url
path('api/jwt/update/', views.UpdateTokenView.as_view()),
第六步 前端部分 在 store中建立一個更新使用者資訊中token的函式
import { ref, computed } from 'vue' import { defineStore } from 'pinia' export const userInfoStore = defineStore('userInfo', () => { const userString = ref(localStorage.getItem("info")) const userDict = computed(() => userString.value ? JSON.parse(userString.value) : null) const userId = computed(() => userDict.value ? userDict.value.id : null) const userName = computed(() => userDict.value ? userDict.value.username : null) const userToken = computed(() => userDict.value ? userDict.value.token : null) function doLogin(info) { localStorage.setItem("info",JSON.stringify(info)); userString.value = JSON.stringify(info) } function Logout(){ localStorage.clear() } function updateUserInfo(new_token) { const userInfoObject = JSON.parse(userString.value); userInfoObject.token = new_token; localStorage.setItem("info",JSON.stringify(userInfoObject)); userString.value = JSON.stringify(userInfoObject) } return {userDict,userId,userName,userToken,doLogin,Logout,updateUserInfo } })
第七步 判斷返回的資料中 過期時間欄位,如果即將過期,就再發起一次更新token的操作
onMounted(function getdata(){ elLoading.value = true _axios.get("/api/project/").then( (res) =>{ console.log("res.data:",res.data.data) console.log("res.data.count:",res.data.data.count) console.log("res.data.results.len:",res.data.data.page_size) console.log("res.data.exp:",res.data.exp_time) if (res.data.exp_time > 0 && res.data.exp_time < 30 ) { console.log("需要去更新token") _axios.get("/api/jwt/update/").then( (res) =>{ console.log(res.data.data) let new_token = res.data.data userdata.updateUserInfo(new_token) } ) } response.value = res.data.data.results elLoading.value = false // 返回資料總數和每頁條數 page.value = { count:res.data.data.count, page_size:res.data.data.page_size } } ).catch((reson) => { console.log("reson:",reson) }) })