Django + drf-jwt 认证 + Vue3 使用 Axios

Axios的安装和配置

  • 安装
pnpm install axios
  • 封装 axios:配置baseUrl,请求拦截,响应拦截
// requests.ts 文件中
// import { Message } from "element-ui";
/****   request.js   ****/
// 导入axios
import axios from "axios";
// 使用element-ui Message做消息提醒
//1. 创建新的axios实例,
const service = axios.create({
  // 公共接口--这里注意后面会讲
  baseURL: "http://127.0.0.1:8000/api/v1",
  // 超时时间 单位是ms,这里设置了3s的超时时间
  timeout: 3 * 1000,
});
// 2.请求拦截器
service.interceptors.request.use(
  (config) => {
    //发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等,根据需求去添加
    // config.data = JSON.stringify(config.data); //数据转化,也可以使用qs转换
    // config.headers = {
    //   "Content-Type": "application/x-www-form-urlencoded", //配置请求头
    // };
    //注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookie
    const token = localStorage.getItem("token"); //这里取token之前,你肯定需要先拿到token,存一下
    if (token) {
      //   config.params = { token: token }; //如果要求携带在参数中
      config.headers = {
        
        // 需要根据 DRF 提供的 JWT 认证来携带Token,前缀也可以是其它的
        
        Authorization: "JWT " + token,
      }; //如果要求携带在请求头中
    }
    console.log("查看请求头", config.headers);
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 3.响应拦截器
service.interceptors.response.use(
  (response) => {
    //接收到响应数据并成功后的一些共有的处理,关闭loading等
    return response;
  },
  (error) => {
    /***** 接收到异常响应的处理开始 *****/
    if (error && error.response) {
      // 1.公共错误处理
      // 2.根据响应码具体处理
      switch (error.response.status) {
        case 400:
          error.message = "错误请求";
          break;
        case 401:
          error.message = "未授权,请重新登录";
          break;
        case 403:
          error.message = "拒绝访问";
          break;
        case 404:
          error.message = "请求错误,未找到该资源";
          window.location.href = "/NotFound";
          break;
        case 405:
          error.message = "请求方法未允许";
          break;
        case 408:
          error.message = "请求超时";
          break;
        case 500:
          error.message = "服务器端出错";
          break;
        case 501:
          error.message = "网络未实现";
          break;
        case 502:
          error.message = "网络错误";
          break;
        case 503:
          error.message = "服务不可用";
          break;
        case 504:
          error.message = "网络超时";
          break;
        case 505:
          error.message = "http版本不支持该请求";
          break;
        default:
          error.message = `连接错误${error.response.status}`;
      }
    } else {
      // 超时处理
      if (JSON.stringify(error).includes("timeout")) {
        console.log("服务器响应超时,请刷新当前页");
      }
      error.message = "连接服务器失败!";
    }
    // 可以配合Element-plus来提示相关信息
    // ELMessage.error(error.message);
    /***** 处理结束 *****/
    //如果不需要错误处理,以上的处理过程都可省略
    // return Promise.resolve(error.response);
    console.log(error.message);
    console.log(1213);
    return Promise.reject(error);
  }
);
//4.导入文件
export default service;

  • ts 中简单的使用,get 和 post 请求
// api.ts
import service from "./requests";

// 按需导出每个 API

// *************************** //
// 请求首页数据
interface Iget {
  [propName: string]: any;
}

export function GetHomeAPI<T>(data: Iget) {
  return service.get<T>("/mdm/slideinfo/", {
    params: data,
  });
}

interface ILogin {
  username: string;
  password: string;
}

export function PostLoginAPI(data: ILogin) {
  return service({
    method: "POST",
    url: "/user/login/auth/",
    data,
  });
}

Vue中使用

<script setup lang="ts">
// 导入 API 接口数据
import { GetHomeAPI, PostLoginAPI } from './api/api'
interface IgetHome {
  "id": number;
  "province": string;
  "county": string;
  "slide_name": string;
  "slide_head": string;
  "slide_type": string;
  "slide_status": number;
  "created_time": string;
  "is_active": boolean;
  "user": number;
}

interface Ilogin {
  "token": string
  "id": number
  "username": string
  "role": boolean
}
function getHome() {
  GetHomeAPI<IgetHome>({}).then(
    (res) => {
      console.log(res)
    }
  ).catch()
}
const getAs = async () => {
  await GetHomeAPI({ id: 1 }).then(
    (res) => {
      console.log(res.data)
    }
  ).catch()
}
const login = () => {
  PostLoginAPI({
    username: "admin",
    password: "521",
  }).then(
    (res) => {
      console.log(res.data)
      localStorage.setItem("token", res.data.token)
    }
  ).catch()
}
</script>

<template>
  <button @click="getHome">getHome</button>
  <button @click="getAs">getAs</button>
  <button @click="login">login</button>
</template>

<style>
</style>

Django后端配置

# settings.py
# 针对 drf 的配置信息, 全局配置 drf的视图的认证和权限
REST_FRAMEWORK = {
    # 指定视图权限
    'DEFAULT_PERMISSION_CLASSES': (
        # 'rest_framework.permissions.AllowAny',  # 默认每个视图,允许任何用户访问
        # 'rest_framework.permissions.IsAuthenticatedOrReadOnly',  # 认证用户可操作,否则只能访问
        'rest_framework.permissions.IsAuthenticated',  # 认证用户可操作,否则只能访问
    ),  # 也可以在每个视图中指明权限类
    # 指定drf认证机制
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # rest_framework_jwt认证, 也可以在每个视图中指明认证类
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
    # 新版drf schema_class默认用的是rest_framework.schemas.openapi.AutoSchema
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}

# 全局配置 drf的视图的限流Throttling

# REST_FRAMEWORK = {
#     'DEFAULT_THROTTLE_CLASSES': (
#         # 限制所有匿名未认证用户,使用IP区分用户
#         'rest_framework.throttling.AnonRateThrottle',
#         # 限制认证用户,使用User id 来区分
#         'rest_framework.throttling.UserRateThrottle'
#     ),
#     'DEFAULT_THROTTLE_RATES': {
#         # 可以使用 second, minute, hour 或day来指明周期
#         'anon': '3/minute',
#         'user': '5/minute'
#     }
# }


# 针对 rest_framework_jwt 的配置信息
JWT_AUTH = {
    'JWT_AUTH_HEADER_PREFIX': 'JWT',  # 设置 请求头中的前缀
    'JWT_RESPONSE_PAYLOAD_HANDLER':
    # 'rest_framework_jwt.utils.jwt_response_payload_handler', # 默认jwt认证成功返回数据
        'user.utils.jwt_response_payload_handler',  # 自定义jwt认证成功返回数据
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=60 * 60 * 3),  # 指明token的有效期, 默认5分
    'JWT_ALLOW_REFRESH': True,  # 允许刷新
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(
        days=7
    ),  # 在多久间隔内可以用旧token来刷新以便获取新的token,默认是7天
}

验证

  • 未登录

在这里插入图片描述

  • 登录前,没有token等

在这里插入图片描述

  • 登录后,保存了token

在这里插入图片描述

  • 登录后,再请求数据

在这里插入图片描述

结合pinia来使用

  • 更改请求拦截
import { ElMessage } from 'element-plus';
// 导入axios
import axios from 'axios';
import { IUser, KEY_USER_ID } from '@/store/modules/useUserStore';
// 使用element-ui Message做消息提醒
//1. 创建新的axios实例,
const service = axios.create({
  // 公共接口--这里注意后面会讲
  baseURL: 'http://127.0.0.1:8000/api/v1',
  // 超时时间 单位是ms,这里设置了5s的超时时间
  timeout: 5 * 1000,
});
// 2.请求拦截器
service.interceptors.request.use(
  (config) => {
    //发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等,根据需求去添加
    // config.data = JSON.stringify(config.data); //数据转化,也可以使用qs转换
    // config.headers = {
    //   "Content-Type": "application/x-www-form-urlencoded", //配置请求头
    // };
    //注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookie
    //这里取token之前,你肯定需要先拿到token,存一下
    try {
      const user = JSON.parse(localStorage.getItem(KEY_USER_ID) || '') as IUser;
      if (user.token) {
        config.headers!['Authorization'] = `JWT ${user.token}`;
      }
    } catch (e) {}
    // if (token) {
    //   //   config.params = { token: token }; //如果要求携带在参数中
    //   config.headers = {
    //     // 需要根据 DRF 提供的 JWT 认证来携带Token,前缀也可以是其它的

    //     Authorization: 'JWT ' + token.token,
    //   }; //如果要求携带在请求头中
    // }
    // console.log('查看请求头', config.headers);
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 3.响应拦截器
service.interceptors.response.use(
  (response) => {
    //接收到响应数据并成功后的一些共有的处理,关闭loading等
    return response;
  },
  (error) => {
    /***** 接收到异常响应的处理开始 *****/
    if (error && error.response) {
      // 1.公共错误处理
      // 2.根据响应码具体处理
      switch (error.response.status) {
        case 400:
          error.message = '错误请求!';
          break;
        case 401:
          error.message = '未授权,请重新登录';
          break;
        case 403:
          error.message = '拒绝访问';
          break;
        case 404:
          error.message = '请求错误,未找到该资源';
          window.location.href = '/NotFound';
          break;
        case 405:
          error.message = '请求方法未允许';
          break;
        case 408:
          error.message = '请求超时';
          break;
        case 500:
          error.message = '服务器端出错';
          break;
        case 501:
          error.message = '网络未实现';
          break;
        case 502:
          error.message = '网络错误';
          break;
        case 503:
          error.message = '服务不可用';
          break;
        case 504:
          error.message = '网络超时';
          break;
        case 505:
          error.message = 'http版本不支持该请求';
          break;
        default:
          error.message = `连接错误${error.response.status}`;
      }
    } else {
      // 超时处理
      if (JSON.stringify(error).includes('timeout')) {
        console.log('服务器响应超时,请刷新当前页');
      }
      error.message = '连接服务器失败!';
    }
    // 可以配合Element-plus来提示相关信息
    ElMessage.error(error.message);
    /***** 处理结束 *****/
    //如果不需要错误处理,以上的处理过程都可省略
    // return Promise.resolve(error.response);
    return Promise.reject(error);
  }
);
//4.导出文件
export default service;

  • pinia中
import { defineStore } from 'pinia';
// 根据自己返回数据来定义接口
export interface IUser {
  id: number;
  username: string;
  token: string;
  role: string;
  email?: string;
}
export const KEY_USER_ID = 'user';
const useUserStore = defineStore({
  id: KEY_USER_ID,
  state: (): IUser => ({
    id: -1,
    username: '',
    role: '',
    email: '',
    token: '',
  }),
  actions: {
    setToken(token: string) {
      this.$state.token = token;
    },
    setID(id: number) {
      this.$state.id = id;
    },
    setRole(role: string) {
      this.$state.role = role;
    },
    // 将接口规范全变为可选项
    login(user: Partial<IUser>) {
      this.$state = {
        ...this.$state,
        ...user,
      };
    },
  },
  // 开启数据缓存至浏览器
  persist: {
    enabled: true,
    strategies: [
      {
        // 自定义名称
        key: KEY_USER_ID,
        // 保存位置,默认保存在sessionStorage
        storage: localStorage,
        // 指定要持久化的数据
        // paths: ['age'],
      },
    ],
  },
});

export default useUserStore;

  • 登录函数中
import useUserStore from '@/store/modules/useUserStore';
import { useRouter } from 'vue-router';
const router = useRouter()
const useUser = useUserStore()

const login = () => {
  PostLoginAPI({
    username: "admin",
    password: "521",
  }).then(
    (res) => {
      console.log(res.data)
      // localStorage.setItem("token", res.data.token)更改如下
      useUser.login(res.data)
    }
  ).catch()
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

而又何羡乎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值