不使用Redux,如何将accessToken传入Axios拦截器?
问题描述
我正在尝试使用手动JWT策略,通过现有Node.js后端对Next.js应用进行授权。后端会颁发access token,我希望通过axios.interceptor为每个请求添加Bearer ${token}签名,相关代码如下:
// utils/axios.ts const axiosPrivate = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, timeout: 1000, withCredentials: true, headers: { 'Content-Type': 'application/json', }, }); axiosPrivate.interceptors.request.use( (config: AxiosRequestConfig): AxiosRequestConfig => { if (config.headers === undefined) { config.headers = {}; } // 无法访问 sessionStorage 或 document.cookies config.headers['Authorization'] = `Bearer ${accessToken}` return config; } ); export { axiosPrivate }
我的问题是:有没有不使用Redux就能获取access token的方法?
我希望在拦截器中实现这一点,这样就能像下面这样进行SSR:
// pages/dashboard.tsx export const getServerSideProps: GetServerSideProps = async (context) => { const res = await axiosPrivate.get('/dashboard'); // 请求已自动签名 return { props: { dashboard: res.data, }, }; };
解决方案
Next.js存在客户端浏览器和服务端SSR两种运行环境,需要针对不同场景分别处理token获取,不用Redux的话可以试试下面几种方案:
1. 给拦截器加外部传参逻辑(兼顾SSR和客户端)
修改axios拦截器,允许从请求配置里传入token,同时兼容客户端环境从本地存储获取:
// utils/axios.ts import axios, { AxiosRequestConfig } from 'axios'; const axiosPrivate = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, timeout: 1000, withCredentials: true, headers: { 'Content-Type': 'application/json', }, }); axiosPrivate.interceptors.request.use( // 扩展配置类型,支持传入token (config: AxiosRequestConfig & { token?: string }): AxiosRequestConfig => { if (!config.headers) config.headers = {}; // 优先用请求时传入的token,客户端环境下再尝试从localStorage取 const token = config.token || (typeof window !== 'undefined' ? window.localStorage.getItem('accessToken') : null); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; } ); export { axiosPrivate }
在SSR的getServerSideProps里,从请求上下文的cookie中拿token并传入:
// pages/dashboard.tsx import { axiosPrivate } from '../utils/axios'; export const getServerSideProps: GetServerSideProps = async (context) => { // 从服务端请求的cookie中提取token(推荐把accessToken存在HttpOnly cookie里) const accessToken = context.req.cookies.accessToken; const res = await axiosPrivate.get('/dashboard', { token: accessToken }); return { props: { dashboard: res.data, }, }; };
2. 封装可传入token的axios实例(更灵活)
直接封装一个创建axios实例的函数,每次使用时传入对应环境的token:
// utils/axios.ts import axios, { AxiosRequestConfig } from 'axios'; export const createAxiosPrivate = (token?: string) => { const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, timeout: 1000, withCredentials: true, headers: { 'Content-Type': 'application/json', // 初始化时直接带上token ...(token && { Authorization: `Bearer ${token}` }) }, }); // 可选:添加响应拦截器处理token过期等逻辑 instance.interceptors.response.use( res => res, async err => { // 这里可以加token刷新逻辑 return Promise.reject(err); } ); return instance; };
SSR场景使用:
// pages/dashboard.tsx import { createAxiosPrivate } from '../utils/axios'; export const getServerSideProps: GetServerSideProps = async (context) => { const accessToken = context.req.cookies.accessToken; const axiosPrivate = createAxiosPrivate(accessToken); const res = await axiosPrivate.get('/dashboard'); return { props: { dashboard: res.data, }, }; };
客户端组件使用:
// components/ClientComponent.tsx 'use client'; import { createAxiosPrivate } from '../utils/axios'; const ClientComponent = () => { const fetchData = async () => { const token = localStorage.getItem('accessToken'); const axiosPrivate = createAxiosPrivate(token); const res = await axiosPrivate.get('/user/profile'); // 处理返回数据 }; return <button onClick={fetchData}>获取用户信息</button>; };
关键注意事项
- 优先把access token存在HttpOnly cookie中,能避免XSS攻击,这种情况下客户端无法通过
document.cookie访问,SSR必须从服务端上下文的req.cookies获取。 - 区分服务端和客户端环境的核心是判断
typeof window !== 'undefined',服务端环境不存在window对象。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

