无需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
相关产品推荐
相关产品推荐

