You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js登录成功后如何将JWT Token传递至/allcoupons页面?

问题描述

我正在开发一个登录应用,前端使用Next.js,后端使用SpringBoot。已实现前端登录功能,调用SpringBoot的/authenticate接口成功返回authToken,目前将其存储在localStorage中,之后跳转到首页/allcoupons。

首页需要展示通过后端另一接口获取的优惠券列表,但该接口调用在getServerSideProps()中,无法访问localStorage里的authToken,导致无法在请求中携带令牌。请问如何将authToken传递至首页的getServerSideProps()中?


解决方案

方案1:使用Cookie存储Token(推荐)

服务端渲染时,getServerSideProps可以访问请求头中的Cookie,因此将Token存在Cookie中是最安全且适配服务端渲染的方案。

修改login.js:

  1. 安装js-cookie库简化Cookie操作:
npm install js-cookie
  1. 导入并替换localStorage存储逻辑:
import Cookies from 'js-cookie';

// ... 其他代码

const handleSubmit = async (e) => {
  // ... 其他代码
  try {
    const response = await axios({
      method: "post",
      headers: {},
      url: "http://localhost:8081/authenticate",
      data: requestbody,
    });

    if (response.status === 200){
      // 替换localStorage为Cookie存储,设置过期时间、安全属性
      Cookies.set('authToken', response.data, { 
        expires: 7, 
        secure: process.env.NODE_ENV === 'production', 
        sameSite: 'strict' 
      });
      router.push("/allcoupons");
    } else {
      alert("invalid credentials")
    }
  } catch (error) {
    // ... 错误处理
  }
};

修改home.js的getServerSideProps:

从请求头的Cookie中提取Token:

export async function getServerSideProps(context) {
  let authToken = '';
  // 从请求头获取Cookie并解析
  if (context.req.headers.cookie) {
    const cookieMap = context.req.headers.cookie.split(';').reduce((acc, cookie) => {
      const [key, value] = cookie.trim().split('=');
      acc[key] = value;
      return acc;
    }, {});
    authToken = cookieMap.authToken || '';
  }

  const res = await axios.get(`http://localhost:8081/couponstore/v1.0/coupons`, {
    headers: {"Authorization": `Bearer ${authToken}`}
  })
  const datafromAPI = res.data

  return { props: { datafromAPI } }
}

方案2:URL参数传递Token(不推荐,仅用于测试)

通过跳转时的URL参数传递Token,此方法存在安全风险(Token会暴露在URL中),生产环境禁止使用。

修改login.js的跳转逻辑:

if (response.status === 200){
  localStorage.setItem("authToken", response.data);
  // 通过query参数传递Token
  router.push({
    pathname: '/allcoupons',
    query: { token: response.data }
  });
}

修改home.js的getServerSideProps:

export async function getServerSideProps(context) {
  const authToken = context.query.token || '';
  
  // 拿到Token后建议立刻存入Cookie/localStorage,并重定向移除URL参数
  if (authToken) {
    return {
      redirect: {
        destination: '/allcoupons',
        permanent: false
      }
    }
  }

  const res = await axios.get(`http://localhost:8081/couponstore/v1.0/coupons`, {
    headers: {"Authorization": `Bearer ${authToken}`}
  })
  const datafromAPI = res.data

  return { props: { datafromAPI } }
}

方案3:客户端获取数据(放弃服务端渲染)

将数据请求从getServerSideProps移到客户端组件中,直接访问localStorage中的Token,但会失去服务端渲染的SEO优势。

修改home.js:

移除getServerSideProps,改用useEffect在客户端请求数据:

const Allcoupons = () => {
  const router = useRouter();
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchCoupons = async () => {
      const authToken = localStorage.getItem("authToken");
      // 无Token则跳回登录页
      if (!authToken) {
        router.push('/login');
        return;
      }
      try {
        const res = await axios.get(`http://localhost:8081/couponstore/v1.0/coupons`, {
          headers: {"Authorization": `Bearer ${authToken}`}
        });
        setData(res.data.coupons);
      } catch (error) {
        console.error('获取优惠券失败', error);
      } finally {
        setLoading(false);
      }
    };

    fetchCoupons();
  }, [router]);

  if (loading) {
    return <div>加载中...</div>;
  }

  // 原组件渲染逻辑,将data替换为state中的data
  return (
    <>
      <section className={`text-gray-600 body-font ${(showModal || showstatusModal) ? "bg-slate-800/10" : ""} banner02 fadeRightMini`}>
        <div className="container px-5 py-24 mx-auto">
          <div className="flex flex-col text-center w-full mb-20">
            {/* ... 原内容 */}
          </div>
          <div className="lg:w-10/12 w-full mx-auto overflow-auto">
            <table className="table-auto w-full text-center whitespace-no-wrap">
              <thead>
                <tr>
                  {/* ... 原内容 */}
                </tr>
              </thead>
              <tbody>
                {data.map((item) => (
                  // ... 原渲染逻辑
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </section>
      <Footer />
    </>
  );
};

export default Allcoupons;

总结

优先选择方案1,既保留服务端渲染的优势,又能安全地在服务端获取Token;如果对SEO没有要求,方案3实现最简单;方案2仅适合临时测试,绝对不能用于生产环境。

内容的提问来源于stack exchange,提问作者MagnusEffect

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 19:20:39