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

无需NextAuth/Iron-Sessions:Next.js对接Spring API自定义JWT认证问询

Next.js 对接 Spring API 自定义 JWT 认证方案

是否必须使用 NextAuth 或 iron-sessions?

不需要。Next.js 本身支持自定义认证逻辑,完全可以不依赖这些第三方库,直接基于后端返回的 JWT 实现会话管理。

自定义认证实现步骤与示例代码

1. 登录逻辑:调用 Spring API 验证账号密码并获取 JWT

在前端创建登录组件,通过 POST 请求提交账号密码到 Next.js 代理 API,再转发至 Spring 后端,成功后存储 JWT(推荐用 HttpOnly Cookie 提升安全性)。

示例登录组件(components/LoginForm.jsx):

'use client';

import { useState } from 'react';

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

  const handleLogin = async (e) => {
    e.preventDefault();
    setError('');

    try {
      const res = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      });

      if (!res.ok) throw new Error('登录失败,请检查账号密码');
      
      // 登录成功后跳转至受保护页面
      window.location.href = '/dashboard';
    } catch (err) {
      setError(err.message);
    }
  };

  return (
    <form onSubmit={handleLogin} style={{ maxWidth: '300px', margin: '2rem auto' }}>
      <div style={{ marginBottom: '1rem' }}>
        <label style={{ display: 'block', marginBottom: '0.5rem' }}>邮箱</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
          style={{ width: '100%', padding: '0.5rem' }}
        />
      </div>
      <div style={{ marginBottom: '1rem' }}>
        <label style={{ display: 'block', marginBottom: '0.5rem' }}>密码</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
          style={{ width: '100%', padding: '0.5rem' }}
        />
      </div>
      {error && <p style={{ color: 'red', margin: '0 0 1rem' }}>{error}</p>}
      <button type="submit" style={{ width: '100%', padding: '0.5rem', backgroundColor: '#0070f3', color: 'white', border: 'none', borderRadius: '4px' }}>登录</button>
    </form>
  );
}

2. 创建 Next.js API 路由转发登录请求

通过 Next.js API 路由转发请求到 Spring 后端,避免跨域问题,并将返回的 JWT 存入 HttpOnly Cookie。

App Router 示例(app/api/auth/login/route.js):

export async function POST(request) {
  const { email, password } = await request.json();

  try {
    // 调用 Spring 登录接口
    const springRes = await fetch('http://你的SpringAPI域名/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    });

    if (!springRes.ok) throw new Error('账号密码验证失败');
    
    const { accessToken, refreshToken } = await springRes.json();

    // 设置 HttpOnly Cookie,防止XSS攻击
    const headers = new Headers();
    headers.append('Set-Cookie', `accessToken=${accessToken}; HttpOnly; Path=/; Max-Age=3600; SameSite=Lax`);
    headers.append('Set-Cookie', `refreshToken=${refreshToken}; HttpOnly; Path=/; Max-Age=86400; SameSite=Lax`);

    return new Response(JSON.stringify({ message: '登录成功' }), {
      status: 200,
      headers
    });
  } catch (err) {
    return new Response(JSON.stringify({ message: err.message }), {
      status: 401
    });
  }
}

Pages Router 示例(pages/api/auth/login.js):

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: '仅支持POST请求' });
  }

  const { email, password } = req.body;

  try {
    const springRes = await fetch('http://你的SpringAPI域名/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    });

    if (!springRes.ok) throw new Error('账号密码验证失败');
    
    const { accessToken, refreshToken } = await springRes.json();

    res.setHeader('Set-Cookie', [
      `accessToken=${accessToken}; HttpOnly; Path=/; Max-Age=3600; SameSite=Lax`,
      `refreshToken=${refreshToken}; HttpOnly; Path=/; Max-Age=86400; SameSite=Lax`
    ]);

    res.status(200).json({ message: '登录成功' });
  } catch (err) {
    res.status(401).json({ message: err.message });
  }
}

3. 受保护路由的认证校验

全局路由校验(App Router 用 Middleware)

创建 middleware.js 全局拦截受保护路由,校验 Cookie 中的 JWT:

import { NextResponse } from 'next/server';

export function middleware(request) {
  const accessToken = request.cookies.get('accessToken')?.value;
  // 定义需要保护的路由列表
  const protectedRoutes = ['/dashboard', '/profile'];
  const isProtected = protectedRoutes.some(route => request.nextUrl.pathname.startsWith(route));

  if (isProtected && !accessToken) {
    // 未登录则跳转至登录页
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*']
};

服务端渲染页面校验(Pages Router)

在受保护页面的 getServerSideProps 中校验 JWT:

export async function getServerSideProps(context) {
  const accessToken = context.req.cookies.accessToken;

  if (!accessToken) {
    return {
      redirect: {
        destination: '/login',
        permanent: false
      }
    };
  }

  try {
    // 调用Spring API验证JWT有效性
    const res = await fetch('http://你的SpringAPI域名/auth/me', {
      headers: { Authorization: `Bearer ${accessToken}` }
    });

    if (!res.ok) throw new Error('JWT已失效');
    const user = await res.json();

    return { props: { user } };
  } catch (err) {
    return {
      redirect: {
        destination: '/login',
        permanent: false
      }
    };
  }
}

export default function Dashboard({ user }) {
  return <div style={{ margin: '2rem' }}>欢迎回来,{user.username}!</div>;
}

4. 登出逻辑

创建登出组件及对应的 API 路由,清除 Cookie:

登出组件(components/LogoutButton.jsx):

'use client';

export default function LogoutButton() {
  const handleLogout = async () => {
    await fetch('/api/auth/logout', { method: 'POST' });
    window.location.href = '/login';
  };

  return <button onClick={handleLogout} style={{ padding: '0.5rem 1rem', cursor: 'pointer' }}>登出</button>;
}

登出 API 路由(app/api/auth/logout/route.js):

export async function POST() {
  const headers = new Headers();
  // 清除Cookie
  headers.append('Set-Cookie', `accessToken=; HttpOnly; Path=/; Max-Age=0`);
  headers.append('Set-Cookie', `refreshToken=; HttpOnly; Path=/; Max-Age=0`);

  return new Response(JSON.stringify({ message: '登出成功' }), {
    status: 200,
    headers
  });
}

关键注意事项

  • 安全性:使用 HttpOnly Cookie 存储 JWT,避免 XSS 攻击;设置 SameSite=Lax 降低 CSRF 风险;确保 Spring API 和 Next.js 都启用 HTTPS。
  • JWT 刷新:实现刷新令牌逻辑,在 accessToken 过期前自动调用 Spring API 获取新令牌,避免用户频繁登录。
  • 跨域处理:通过 Next.js API 路由转发请求,无需在 Spring 后端配置复杂的跨域规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:25:19