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

如何在Next.js中结合AuthContext与Middleware使用Cookie?

解决Next.js AuthContext与Cookie设置的组件类型冲突问题

核心思路

把Cookie设置逻辑完全放到服务端(Server Action),客户端组件只负责触发登录请求、同步AuthContext状态,彻底避免在客户端组件里调用服务端专属API(比如next/headers)。

具体实现步骤

1. 编写Server Action处理登录与Cookie设置

在服务端专属文件(比如app/actions/auth.actions.ts)里实现登录逻辑,这里可以安全使用next/headers操作Cookie:

'use server';

import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';

export async function login(formData: FormData) {
  // 从表单提取账号密码
  const email = formData.get('email') as string;
  const password = formData.get('password') as string;

  // 调用身份提供商的验证接口
  const authResponse = await fetch('https://你的身份验证地址/login', {
    method: 'POST',
    body: JSON.stringify({ email, password }),
    headers: { 'Content-Type': 'application/json' },
  });

  if (!authResponse.ok) {
    throw new Error('登录失败,请检查账号密码');
  }

  const { accessToken, userInfo } = await authResponse.json();

  // 设置HttpOnly Cookie(防止XSS攻击,提升安全性)
  cookies().set('accessToken', accessToken, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    maxAge: 60 * 60 * 24, // 有效期1天
    path: '/',
  });

  // 登录成功后重定向到目标页面
  redirect('/');
}

// 登出逻辑:清除Cookie
export async function logout() {
  cookies().delete('accessToken');
  redirect('/login');
}

2. 改造AuthContext客户端组件

AuthContext只负责管理客户端用户状态,不再处理Cookie操作,通过调用Server Action完成登录/登出,同步状态:

'use client';

import { createContext, useContext, useState, useEffect } from 'react';
import { login as serverLogin, logout as serverLogout } from '@/app/actions/auth.actions';

type AuthContextType = {
  user: any | null;
  login: (formData: FormData) => Promise<void>;
  logout: () => Promise<void>;
};

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<any | null>(null);

  // 调用服务端登录Action
  const login = async (formData: FormData) => {
    try {
      await serverLogin(formData);
      // 登录成功后拉取用户信息同步到Context
      const res = await fetch('/api/user');
      if (res.ok) setUser(await res.json());
    } catch (err) {
      console.error(err);
    }
  };

  // 调用服务端登出Action
  const logout = async () => {
    await serverLogout();
    setUser(null);
  };

  // 组件挂载时同步当前用户状态
  useEffect(() => {
    const fetchUser = async () => {
      const res = await fetch('/api/user');
      if (res.ok) setUser(await res.json());
    };
    fetchUser();
  }, []);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth必须在AuthProvider内部使用');
  return context;
};

3. 登录表单组件调用逻辑

在客户端表单里直接调用AuthContext的登录方法即可:

'use client';

import { useAuth } from '@/app/context/AuthContext';

export default function LoginForm() {
  const { login } = useAuth();

  return (
    <form action={login}>
      <input type="email" name="email" placeholder="邮箱" required />
      <input type="password" name="password" placeholder="密码" required />
      <button type="submit">登录</button>
    </form>
  );
}

4. Middleware验证Cookie

在middleware.ts里读取Cookie并验证token有效性:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  const accessToken = request.cookies.get('accessToken')?.value;
  // 无需验证的公开路由
  const publicPaths = ['/login', '/signup', '/_next', '/favicon.ico'];
  const isPublic = publicPaths.some(path => request.nextUrl.pathname.startsWith(path));

  if (isPublic) return NextResponse.next();

  // 无token则重定向到登录页
  if (!accessToken) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 调用身份提供商验证token有效性
  const validateRes = await fetch('https://你的身份验证地址/validate-token', {
    method: 'POST',
    body: JSON.stringify({ accessToken }),
    headers: { 'Content-Type': 'application/json' },
  });

  // token无效则清除Cookie并重定向
  if (!validateRes.ok) {
    const response = NextResponse.redirect(new URL('/login', request.url));
    response.cookies.delete('accessToken');
    return response;
  }

  // 验证通过,允许访问
  return NextResponse.next();
}

// 指定生效的路由范围
export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

关键注意事项

  • HttpOnly Cookie:必须开启httpOnly: true,禁止前端JS读取Cookie,避免XSS攻击风险。
  • 服务端状态同步:客户端用户状态通过调用服务端接口(比如/api/user)获取,不要直接从Cookie读取敏感信息。
  • Server Action安全性:Server Action默认运行在服务端,无需额外配置CORS,不用担心跨域问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:33:11