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

Next.js(App Router)对接Express-Session外部API的鉴权与路由保护问题

Next.js App Router 对接Express Session API 实现登录与路由保护

1. 登录流程实现

前端登录组件(客户端组件)

创建客户端组件处理登录表单,调用Express登录接口时必须开启credentials: 'include',确保浏览器能保存返回的Session Cookie:

'use client';

import { useState } from 'react';

export default function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      const res = await fetch('http://your-express-api.com/api/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        credentials: 'include', // 关键:允许携带Cookie
        body: JSON.stringify({ email, password }),
      });

      if (res.ok) {
        // 登录成功,跳转到受保护页面
        window.location.href = '/dashboard';
      } else {
        const error = await res.json();
        alert(error.message || '登录失败');
      }
    } catch (err) {
      alert('网络错误,请重试');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="邮箱"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="密码"
        required
      />
      <button type="submit">登录</button>
    </form>
  );
}

Express端关键配置

确保Express的CORS和Session配置允许跨域Cookie传递:

const express = require('express');
const session = require('express-session');
const cors = require('cors');
const app = express();

// CORS配置:允许Next.js域名,允许携带凭证
app.use(cors({
  origin: 'http://your-nextjs-app.com', // 替换成你的Next.js域名
  credentials: true,
}));

// Session配置:注意Cookie的跨域参数
app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS
    sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', // 跨域时设为none
    maxAge: 24 * 60 * 60 * 1000, // 1天有效期
  },
}));

// 登录接口示例
app.post('/api/login', (req, res) => {
  const { email, password } = req.body;
  // 这里替换成你的用户验证逻辑
  if (email === 'user@example.com' && password === 'password') {
    req.session.user = { id: 1, email }; // 将用户信息存入session
    res.json({ message: '登录成功' });
  } else {
    res.status(401).json({ message: '账号或密码错误' });
  }
});

// 验证用户状态的接口(供Next.js调用)
app.get('/api/me', (req, res) => {
  if (req.session.user) {
    res.json(req.session.user);
  } else {
    res.status(401).json({ message: '未登录' });
  }
});

2. 用户状态验证工具

创建一个服务端工具函数,用于在服务端验证用户Session状态:

// app/lib/auth.ts
export async function getCurrentUser() {
  try {
    const res = await fetch('http://your-express-api.com/api/me', {
      credentials: 'include', // 携带Session Cookie
    });

    if (res.ok) {
      return await res.json();
    }
    return null;
  } catch (err) {
    return null;
  }
}

3. 路由保护实现

方式一:使用Middleware全局拦截(推荐)

在项目根目录创建middleware.ts,全局拦截受保护路由,验证用户状态:

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

export async function middleware(request: NextRequest) {
  // 定义需要保护的路由前缀
  const protectedRoutes = ['/dashboard', '/profile'];
  const isProtectedRoute = protectedRoutes.some(route => request.nextUrl.pathname.startsWith(route));

  if (!isProtectedRoute) {
    return NextResponse.next();
  }

  // 调用Express的验证接口
  const res = await fetch('http://your-express-api.com/api/me', {
    headers: {
      Cookie: request.headers.get('Cookie') || '', // 传递请求中的Cookie
    },
  });

  if (!res.ok) {
    // 未登录,重定向到登录页
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

// 指定Middleware生效的路由
export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*'],
};

方式二:在服务端页面组件中验证

对于单个受保护页面,可以直接在服务端组件中调用验证函数,未登录则重定向:

// app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { getCurrentUser } from '../lib/auth';

export default async function DashboardPage() {
  const user = await getCurrentUser();

  if (!user) {
    redirect('/login');
  }

  return (
    <div>
      <h1>欢迎,{user.email}</h1>
      <p>这是你的仪表盘</p>
    </div>
  );
}

公共路由

所有未被Middleware或页面组件拦截的路由(如/、/login、/about)默认作为公共路由,无需验证即可访问。

内容的提问来源于stack exchange,提问作者Ertan Özdemir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:13:17