drf中jwt应用
第一步 在登录类中生成 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) }) })