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

Next.js 13中如何跨组件共享已登录用户数据?

实现Next.js 13全局共享登录用户数据及状态同步

一、后端先补充用户信息接口

因为HttpOnly Cookie前端无法直接读取,必须通过后端接口验证JWT并返回用户数据。在NestJS里新增一个接口:

// src/auth/auth.controller.ts
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller('api/auth')
export class AuthController {
  // 用JWT Guard验证Cookie中的token
  @Get('me')
  @UseGuards(AuthGuard('jwt'))
  getCurrentUser(@Req() req) {
    // req.user是JWT验证后解析出的用户数据,返回前端需要的字段
    return {
      id: req.user.id,
      username: req.user.username,
      email: req.user.email
    };
  }
}

注意要确保你的JWT策略已经配置成从Cookie中提取token(默认是从Authorization头,需要调整):

// src/auth/jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromExtractors([
        // 从Cookie中提取JWT,这里的cookie名要和登录接口设置的一致
        (req) => req.cookies?.access_token
      ]),
      ignoreExpiration: false,
      secretOrKey: process.env.JWT_SECRET,
    });
  }

  async validate(payload: any) {
    return { id: payload.sub, username: payload.username, email: payload.email };
  }
}

二、前端全局状态管理(App Router)

用React Context实现轻量全局状态,同步Cookie的有效性:

1. 创建Auth上下文

新建app/context/AuthContext.tsx,记得加'use client'指令:

'use client';

import { createContext, useContext, useEffect, useState } from 'react';

type User = {
  id: string;
  username: string;
  email: string;
} | null;

type AuthContextType = {
  user: User;
  fetchUser: () => Promise<void>;
  logout: () => Promise<void>;
};

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

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

  // 调用后端接口获取用户信息
  const fetchUser = async () => {
    try {
      const res = await fetch('/api/auth/me', {
        credentials: 'include', // 必须带这个,否则Cookie不会被发送
      });
      if (res.ok) {
        const data = await res.json();
        setUser(data);
      } else {
        // 401或其他错误,说明Cookie无效/过期
        setUser(null);
      }
    } catch (err) {
      setUser(null);
    }
  };

  // 登出逻辑:调用后端接口清除Cookie,然后重置用户状态
  const logout = async () => {
    await fetch('/api/auth/logout', {
      method: 'POST',
      credentials: 'include',
    });
    setUser(null);
  };

  // 页面加载时自动获取用户状态
  useEffect(() => {
    fetchUser();
  }, []);

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

// 自定义钩子,简化上下文调用
export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}

2. 在根布局中包裹应用

修改app/layout.tsx,用AuthProvider包裹所有子组件:

import { AuthProvider } from './context/AuthContext';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="zh-CN">
      <body>
        <AuthProvider>{children}</AuthProvider>
      </body>
    </html>
  );
}

三、在组件中使用用户状态

比如导航栏显示欢迎语:

'use client';

import { useAuth } from '../context/AuthContext';

export default function Navbar() {
  const { user, logout } = useAuth();

  return (
    <nav>
      <div>
        {user ? (
          <>
            <span>欢迎回来,{user.username}!</span>
            <button onClick={logout}>登出</button>
          </>
        ) : (
          <a href="/login">登录</a>
        )}
      </div>
    </nav>
  );
}

个人页面验证登录状态:

'use client';

import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useAuth } from '../context/AuthContext';

export default function ProfilePage() {
  const { user } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (!user) {
      router.push('/login');
    }
  }, [user, router]);

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

  return (
    <div>
      <h1>个人主页</h1>
      <p>用户名:{user.username}</p>
      <p>邮箱:{user.email}</p>
    </div>
  );
}

四、处理Cookie过期/删除的情况

  1. 统一接口拦截:封装一个带认证的fetch工具,自动处理401状态:
'use client';

import { useAuth } from './context/AuthContext';

export function useAuthFetch() {
  const { fetchUser } = useAuth();

  const authFetch = async (url: string, options: RequestInit = {}) => {
    const res = await fetch(url, {
      ...options,
      credentials: 'include',
    });

    if (res.status === 401) {
      // Cookie过期/无效,重新获取用户状态(会设为null)
      await fetchUser();
    }

    return res;
  };

  return authFetch;
}

之后所有需要认证的接口都用这个钩子调用,比如获取用户帖子:

'use client';

import { useEffect, useState } from 'react';
import { useAuthFetch } from '../hooks/useAuthFetch';
import { useAuth } from '../context/AuthContext';

type Post = {
  id: string;
  title: string;
  content: string;
};

export default function UserPosts() {
  const { user } = useAuth();
  const authFetch = useAuthFetch();
  const [posts, setPosts] = useState<Post[]>([]);

  useEffect(() => {
    if (!user) return;

    const getPosts = async () => {
      const res = await authFetch(`/api/users/${user.id}/posts`);
      if (res.ok) {
        const data = await res.json();
        setPosts(data);
      }
    };

    getPosts();
  }, [user, authFetch]);

  return (
    <div>
      <h2>我的帖子</h2>
      {posts.map(post => (
        <div key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.content}</p>
        </div>
      ))}
    </div>
  );
}
  1. 定时校验状态:如果需要更高的实时性,可以在全局上下文里加定时任务,比如每隔5分钟调用一次fetchUser,检查Cookie有效性(注意不要过于频繁,避免接口压力)。

五、服务器端验证(可选)

如果需要在服务器组件中验证用户状态,可以用Next.js的cookies()函数获取Cookie,然后调用后端接口:

// app/profile/page.tsx(服务器组件)
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

export default async function ProfilePage() {
  const cookieStore = cookies();
  const accessToken = cookieStore.get('access_token');

  if (!accessToken) {
    // 未登录,重定向到登录页
    return redirect('/login');
  }

  // 调用后端接口验证并获取用户信息
  const res = await fetch('http://your-nestjs-api/api/auth/me', {
    headers: {
      Cookie: `access_token=${accessToken.value}`,
    },
    cache: 'no-store',
  });

  if (!res.ok) {
    return redirect('/login');
  }

  const user = await res.json();

  return (
    <div>
      <h1>个人主页</h1>
      <p>用户名:{user.username}</p>
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:30:58