You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Laravel(API)+Next.js构建HR管理系统:认证与前后端对接全流程指导请求

Laravel(API)+Next.js构建HR管理系统:认证与前后端对接全流程指导

嘿,我刚帮几个朋友搭过类似的Laravel+Next.js HR系统,你的需求我太懂了!结合你已经配置好Laravel Breeze的情况,我给你梳理一套标准且新手友好的对接流程,一步步来:


一、先把Laravel后端的API配置捋顺(适配Next.js)

你已经装了Breeze+Sanctum,只需要调整几个关键配置让它支持Next.js的跨域请求:

  1. 环境变量配置
    打开.env文件,更新这两个参数(假设Next.js跑在localhost:3000,Laravel在localhost:8000):
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:3000
  1. CORS跨域配置
    修改config/cors.php,确保允许携带凭证、开放必要路径:
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_origins' => ['http://localhost:3000'],
    'allowed_origins_patterns' => [],
    'allowed_methods' => ['*'],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true, // 这个必须开,否则cookie传不过去
];
  1. 确认API路由的认证中间件
    Laravel Breeze已经帮你生成了routes/api.php的认证组,你只需要把HR系统的私有接口(比如员工管理、部门管理)放进这个组里:
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
    // 把你的HR接口加在这里
    Route::apiResource('employees', EmployeeController::class);
    Route::apiResource('departments', DepartmentController::class);
});

二、Next.js前端的新手友好型项目结构

给你推荐一个清晰且易维护的结构,避免后期代码混乱:

app/
  - auth/
    - login/
      - page.jsx  # 登录页面
  - dashboard/
    - page.jsx  # HR系统后台首页
    - layout.jsx # 后台专属布局(侧边栏、导航栏)
  - layout.jsx  # 全局根布局
  - page.jsx    # 未登录时的首页
lib/
  - api.js      # 封装所有API请求,统一处理token、错误
  - auth.js     # 认证工具函数:登录、登出、获取用户信息
middleware.js # Next.js路由中间件,管控未登录跳转

三、Token认证全流程(登录、登出、过期处理)

因为你用的是Laravel Breeze的SPA模式,优先用Sanctum的Cookie认证(比存token到localStorage安全10倍),流程如下:

1. 登录流程

Next.js前端需要先获取Sanctum的CSRF Cookie,再发送登录请求:
先在lib/api.js封装通用请求和登录函数:

// 封装带认证的API请求
export async function apiRequest(url, options = {}) {
  const baseUrl = process.env.NEXT_PUBLIC_API_URL; // 根目录.env里配置这个变量
  const res = await fetch(`${baseUrl}${url}`, {
    credentials: 'include', // 自动携带认证Cookie
    headers: {
      'Content-Type': 'application/json',
      ...options.headers,
    },
    ...options,
  });

  // 处理登录过期/未授权
  if (res.status === 401) {
    await logoutUser();
    window.location.href = '/auth/login';
    throw new Error('登录已过期,请重新登录');
  }

  if (!res.ok) {
    const error = await res.json();
    throw new Error(error.message || '请求失败');
  }

  return res.json();
}

// 登录函数
export async function loginUser(email, password) {
  // 先获取Sanctum的CSRF Cookie
  await fetch(`${process.env.NEXT_PUBLIC_API_URL}/sanctum/csrf-cookie`, {
    credentials: 'include',
  });

  // 发送登录请求到Laravel Breeze的API接口
  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/login`, {
    method: 'POST',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ email, password }),
  });

  if (!res.ok) {
    const error = await res.json();
    throw new Error(error.message || '登录失败');
  }

  return res.json();
}

// 登出函数
export async function logoutUser() {
  await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/logout`, {
    method: 'POST',
    credentials: 'include',
  });
}

然后在登录页面app/auth/login/page.jsx调用这个函数:

'use client';

import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { loginUser } from '@/lib/api';

export default function LoginPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [errorMsg, setErrorMsg] = useState('');
  const router = useRouter();

  const handleSubmit = async (e) => {
    e.preventDefault();
    setErrorMsg('');
    try {
      await loginUser(email, password);
      router.push('/dashboard'); // 登录成功跳后台
    } catch (err) {
      setErrorMsg(err.message);
    }
  };

  return (
    <div className="max-w-md mx-auto mt-16 p-6 border rounded-lg shadow">
      <h2 className="text-2xl font-bold mb-6 text-center">HR管理系统登录</h2>
      <form onSubmit={handleSubmit}>
        <div className="mb-4">
          <label className="block text-sm font-medium mb-1">邮箱</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            className="w-full p-2 border rounded-md"
            required
          />
        </div>
        <div className="mb-4">
          <label className="block text-sm font-medium mb-1">密码</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            className="w-full p-2 border rounded-md"
            required
          />
        </div>
        {errorMsg && <p className="text-red-500 text-sm mb-4">{errorMsg}</p>}
        <button
          type="submit"
          className="w-full bg-blue-500 text-white p-2 rounded-md hover:bg-blue-600 transition"
        >
          登录
        </button>
      </form>
    </div>
  );
}

2. 登出流程

在后台布局或者导航栏里加个登出按钮,调用logoutUser函数即可:

import { logoutUser } from '@/lib/api';
import { useRouter } from 'next/navigation';

function LogoutButton() {
  const router = useRouter();
  const handleLogout = async () => {
    await logoutUser();
    router.push('/auth/login');
  };
  return <button onClick={handleLogout}>退出登录</button>;
}

3. 自动处理登录过期

上面的apiRequest函数已经帮你做了:只要后端返回401未授权,就自动登出并跳转到登录页,不用手动在每个页面判断。


四、前后端中间件:管控访问权限

1. Laravel后端:保护私有API

刚才已经说了,把需要登录才能访问的API放进auth:sanctum中间件组里,这样未登录的请求会直接返回401。

2. Next.js前端:管控页面访问

用Next.js的middleware.js实现:未登录用户不能进后台,已登录用户不能进登录页。
先在lib/auth.js写个获取当前用户的函数:

import { apiRequest } from './api';

export async function getCurrentUser() {
  try {
    const user = await apiRequest('/api/user');
    return user;
  } catch (err) {
    return null;
  }
}

然后创建middleware.js

import { NextResponse } from 'next/server';
import { getCurrentUser } from './lib/auth';

export async function middleware(request) {
  const user = await getCurrentUser();
  const isAuthPage = request.nextUrl.pathname.startsWith('/auth');
  const isDashboardPage = request.nextUrl.pathname.startsWith('/dashboard');

  // 未登录且访问后台,跳登录页
  if (!user && isDashboardPage) {
    return NextResponse.redirect(new URL('/auth/login', request.url));
  }

  // 已登录且访问登录页,跳后台
  if (user && isAuthPage) {
    return NextResponse.redirect(new URL('/dashboard', request.url));
  }

  return NextResponse.next();
}

// 指定要应用中间件的路由
export const config = {
  matcher: ['/dashboard/:path*', '/auth/:path*'],
};

五、Next.js的认证状态持久化(安全优先)

因为用了Sanctum的Cookie认证,不需要把token存在localStorage/sessionStorage(不安全,容易被XSS攻击),只需要用React Context管理全局用户状态,方便页面快速获取用户信息:

  1. app目录下创建AuthProvider.jsx
'use client';

import { createContext, useContext, useEffect, useState } from 'react';
import { getCurrentUser } from '@/lib/auth';
import { loginUser, logoutUser } from '@/lib/api';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  // 页面加载时自动获取当前用户
  useEffect(() => {
    async function fetchUser() {
      const currentUser = await getCurrentUser();
      setUser(currentUser);
      setLoading(false);
    }
    fetchUser();
  }, []);

  // 封装登录方法,成功后更新用户状态
  const handleLogin = async (email, password) => {
    const userData = await loginUser(email, password);
    setUser(userData.user);
  };

  // 封装登出方法,成功后清空用户状态
  const handleLogout = async () => {
    await logoutUser();
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, loading, login: handleLogin, logout: handleLogout }}>
      {!loading && children}
    </AuthContext.Provider>
  );
}

// 自定义Hook,方便页面调用
export function useAuth() {
  return useContext(AuthContext);
}
  1. 在全局根布局app/layout.jsx里包裹所有页面:
import { AuthProvider } from './AuthProvider';

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

这样你在任何页面都可以用useAuth()快速获取用户状态、调用登录登出:

import { useAuth } from '@/app/AuthProvider';

function DashboardPage() {
  const { user } = useAuth();
  return <h1>欢迎回来,{user.name}!</h1>;
}

最后总结

这套流程完全适配你已有的Laravel Breeze配置,核心是用Sanctum的Cookie认证替代传统的token存储,既安全又省掉了手动管理token的麻烦。Next.js的结构清晰,新手也能快速上手,后续加HR功能只需要往对应目录里加页面和API就行。

如果后续要支持移动端APP,再扩展Sanctum的个人访问令牌功能就行,现在的SPA场景用Cookie认证是最优解。有细节问题随时问!

火山引擎 最新活动