前端请求头

# 前端请求头

前端调用后端接口时,通常需要配置请求头(Headers),这些头信息用于身份验证、跨域控制、数据格式定义等。

# 1.HTTP 请求头的作用

HTTP 请求头(Request Headers)是客户端(浏览器、前端应用)发送 HTTP 请求时,附加的键值对信息。

作用包括:

  • 身份认证Authorization):携带 Token 让后端识别用户身份
  • 数据格式声明Content-Type / Accept):告诉服务器要发送什么格式的数据、希望返回什么格式的数据
  • 安全性Referer / Origin):限制请求来源,防止 CSRF 攻击
  • 性能优化Cache-Control):控制缓存策略,减少不必要的网络请求
  • 跨域请求控制Access-Control-Allow-Origin):后端允许特定域访问资源

# 2. 常见的请求头字段

请求头字段 作用 示例值
Authorization 认证 Token Bearer eyJhbGciOiJIUzI1NiIsInR5cCI...
Content-Type 请求体格式 application/json
Accept 期望的返回格式 application/json
Referer 请求来源 https://example.com
Origin 请求发起者 https://my-frontend.com
User-Agent 客户端信息 Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Cache-Control 缓存策略 no-cache, no-store, must-revalidate
X-Requested-With 标记 AJAX 请求 XMLHttpRequest
APP-DOMAIN 自定义应用标识 my-app-domain

# 3. 在 Vue 3 中封装全局 Axios

# (1) 安装 Axios

npm install axios

# (2) 创建 Axios 实例

src/utils/request.ts(或 request.jsapi/index.ts)封装 Axios,设置全局请求头:

import axios from "axios";
import { getToken, refreshToken } from "@/utils/auth"; // 获取 & 刷新 Token

// 创建Axios实例
const service = axios.create({
    baseURL: import.meta.env.VITE_API_BASE_URL, // 读取环境变量配置
    timeout: 10000, // 设置超时时间10s
   	headers: {
    	"Content-Type": "application/json", // 发送 JSON 格式数据
    	"APP-DOMAIN": "my-app", // 自定义请求头
    },
})

// 请求拦截器:在请求发送前自动添加 Token
service.interceptors.request.use(
  (config) => {
    const token = getToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 响应拦截器:自动处理错误 & 刷新 Token
service.interceptors.response.use(
  async (response) => {
    return response.data; // 直接返回后端返回的数据
  },
  async (error) => {
    const { response } = error;
    if (response) {
      // **处理 401 未授权错误,尝试刷新 Token**
      if (response.status === 401) {
        const newToken = await refreshToken();
        if (newToken) {
          error.config.headers.Authorization = `Bearer ${newToken}`;
          return service(error.config); // 重新请求
        }
      }
    }
    return Promise.reject(error);
  }
);

export default service;

# 4.在 Vue 组件中使用封装的 Axios

import http from "@/utils/request";

const fetchAuthors = async () => {
  try {
    const response = await http.post("/v1/site_ugc/author/get", {
      phone_number: "",
      status: 1,
      page: 1,
      size: 10,
    });
    console.log("API 返回的数据:", response);
  } catch (error) {
    console.error("请求失败:", error);
  }
};
fetchAuthors();

# 5. 如何在 Vue 全局注册 Axios

# (1) Vue 3 方式

main.ts 注册 Axios,让所有组件可以直接使用:

import { createApp } from "vue";
import App from "./App.vue";
import http from "@/utils/request";

const app = createApp(App);
app.config.globalProperties.$http = http; // 挂载到 Vue 全局
app.mount("#app");

在组件中直接使用:

this.$http.get("/api/data").then((res) => console.log(res));

# (2) Vue 3 + Pinia 全局存储

如果你的项目使用 Pinia 管理状态,可以在 store 里全局管理 Axios

import { defineStore } from "pinia";
import http from "@/utils/request";

export const useApiStore = defineStore("api", {
  state: () => ({
    http,
  }),
});

在组件中:

import { useApiStore } from "@/store/api";

const api = useApiStore();
api.http.get("/api/data").then((res) => console.log(res));

# 6. 如何查看请求头?

# (1) Chrome 开发者工具

  1. 打开网页,按 F12Ctrl + Shift + I
  2. 进入 Network(网络) 选项卡
  3. 选择 Fetch/XHR
  4. 点击某个请求,在 Headers 里查看 Request Headers

# (2) Console 打印

Axios 请求拦截器里:

service.interceptors.request.use((config) => {
  console.log("请求头:", config.headers);
  return config;
});

# 7. 进阶优化

# (1) 防止重复请求

可以使用 axios-cancel 取消重复请求:

import AxiosCancel from "@/utils/axiosCancel";

const axiosCancel = new AxiosCancel();
service.interceptors.request.use((config) => {
  axiosCancel.addPending(config);
  return config;
});

service.interceptors.response.use(
  (response) => {
    axiosCancel.removePending(response.config);
    return response;
  },
  (error) => {
    axiosCancel.removePending(error.config);
    return Promise.reject(error);
  }
);

# (2) 自动刷新 Token

如果 Token 过期,可以使用 refreshToken 自动续签:

import { getToken, refreshToken } from "@/utils/auth";

service.interceptors.response.use(
  async (response) => response,
  async (error) => {
    if (error.response.status === 401) {
      const newToken = await refreshToken();
      if (newToken) {
        error.config.headers.Authorization = `Bearer ${newToken}`;
        return service(error.config); // 重新请求
      }
    }
    return Promise.reject(error);
  }
);

# 登录和验证机制

# 1. 登录流程

一般前端的登录流程如下:

  1. 用户输入 用户名+密码
  2. 前端加密(可选)后,发送到后端
  3. 后端验证用户信息,返回 Token(JWT)
  4. 前端存储 TokenlocalStorage/sessionStorage/cookie
  5. 之后的请求都在请求头中携带 Token
  6. 服务器验证 Token,返回相应数据

流程图:

复制编辑[用户登录] -> [发送账号密码] -> [后端验证] -> [返回 Token] 
    -> [前端存储 Token] -> [请求时携带 Token] -> [后端验证 Token]

# 2. Token 认证(JWT)

JWT(JSON Web Token)是一种常见的 Token 认证方式,Token 通常包含:

{
  "alg": "HS256",
  "typ": "JWT"
}
.
{
  "user_id": "12345",
  "role": "admin",
  "exp": 1710000000
}
.
"签名"

其中:

  • Header(头部):算法信息
  • Payload(载荷):存储用户 ID、角色、过期时间
  • Signature(签名):防止篡改

# 3. Vue 实现登录

# (1) 创建 API 登录请求

api/auth.ts 里封装登录接口:

import http from "@/utils/request";

export const login = (data: { username: string; password: string }) => {
  return http.post("/api/login", data);
};

# (2) Vue 组件实现登录

<script setup>
import { ref } from "vue";
import { login } from "@/api/auth";
import { useAuthStore } from "@/store/auth";
import { useRouter } from "vue-router";

const username = ref("");
const password = ref("");
const authStore = useAuthStore();
const router = useRouter();

const handleLogin = async () => {
  try {
    const res = await login({ username: username.value, password: password.value });
    authStore.setToken(res.token); // 存储 Token
    router.push("/dashboard"); // 登录成功跳转
  } catch (error) {
    console.error("登录失败:", error);
  }
};
</script>

<template>
  <div>
    <input v-model="username" placeholder="用户名" />
    <input v-model="password" type="password" placeholder="密码" />
    <button @click="handleLogin">登录</button>
  </div>
</template>

# 4. 前端存储 Token

常见存储方式:

方式 特点
localStorage 页面刷新后仍存在,但易被 XSS 攻击
sessionStorage 仅在会话期间有效,关闭浏览器后清除
cookie 可设置 HttpOnly 避免 XSS,但请求会自动携带,可能被 CSRF 攻击

# 封装 Token 读取 & 存储

utils/auth.ts 里:

const TOKEN_KEY = "access_token";

export const getToken = () => localStorage.getItem(TOKEN_KEY);
export const setToken = (token: string) => localStorage.setItem(TOKEN_KEY, token);
export const removeToken = () => localStorage.removeItem(TOKEN_KEY);

如果是使用cookies

  1. 直接使用 document.cookie

    const TOKEN_KEY = "access_token";
    
    // 设置 Token(带 HttpOnly 和 Secure 需后端设置)
    export const setToken = (token: string, expiresDays = 7) => {
      const date = new Date();
      date.setTime(date.getTime() + expiresDays * 24 * 60 * 60 * 1000); // 过期时间
      document.cookie = `${TOKEN_KEY}=${token}; expires=${date.toUTCString()}; path=/`;
    };
    
    // 获取 Token
    export const getToken = () => {
      const cookies = document.cookie.split("; ");
      for (const cookie of cookies) {
        const [key, value] = cookie.split("=");
        if (key === TOKEN_KEY) return value;
      }
      return null;
    };
    
    // 移除 Token
    export const removeToken = () => {
      document.cookie = `${TOKEN_KEY}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
    };
    
    
  2. 使用 js-cookie(推荐)

安装 js-cookie

npm install js-cookie

封装 Token 操作

import Cookies from "js-cookie";

const TOKEN_KEY = "access_token";

// 设置 Token,带过期时间(7 天)
export const setToken = (token: string) => {
  Cookies.set(TOKEN_KEY, token, { expires: 7, path: "/" });
};

// 获取 Token
export const getToken = () => Cookies.get(TOKEN_KEY);

// 移除 Token
export const removeToken = () => Cookies.remove(TOKEN_KEY);
  1. localStorage vs. Cookies
存储方式 是否支持 HttpOnly 是否支持跨域 适用场景
localStorage ❌ 不支持 ❌ 仅限同源 仅前端可访问,适合存非敏感信息
Cookies ✅ 可设置 HttpOnly ✅ 可跨子域 适用于 Token 存储,安全性更高

如果后端启用了 HttpOnly,则前端无法直接获取 document.cookie,需要改用后端验证 Cookie

# 5. 请求拦截器自动携带 Token

request.ts(封装的 Axios 里),请求时自动添加 Authorization 头:

service.interceptors.request.use(
  (config) => {
    const token = getToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

# 7. Vue 全局管理用户登录状态

# (1) 使用 Pinia 管理 Token

import { defineStore } from "pinia";
import { getToken, setToken, removeToken } from "@/utils/auth";

export const useAuthStore = defineStore("auth", {
  state: () => ({
    token: getToken() || "",
  }),
  actions: {
    setToken(token: string) {
      this.token = token;
      setToken(token);
    },
    logout() {
      this.token = "";
      removeToken();
    },
  },
});

# (2) 在组件中使用

import { useAuthStore } from "@/store/auth";

const authStore = useAuthStore();
if (!authStore.token) {
  console.log("用户未登录");
}

\8. 权限控制

# 9. 退出登录

# (1) 清除 Token

const handleLogout = () => {
  authStore.logout();
  router.push("/login");
};