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

Laravel Breeze+Next.js Google登录及Next.js认证中间件配置

已实现内容

Laravel Breeze控制器代码

public function handleAuthCallback(): JsonResponse
{
    /** @var SocialiteUser $socialiteUser */
    try {
        $socialiteUser = Socialite::driver('google')->stateless()->user();
    } catch (ClientException $e) {
        return response()->json(['error' => 'Invalid credentials provided.'], 422);
    }
    /** @var User $user */
    $user = User::query()
        ->firstOrCreate(
            [
                'email' => $socialiteUser->getEmail(),
            ],
            [
                'email_verified_at' => now(),
                'name' => $socialiteUser->getName(),
                'google_id' => $socialiteUser->getId(),
                'password' => Hash::make(Str::random(40))
            ]
        );
    event(new Registered($user));
    
    Auth::login($user);
    
    return response()->json([
        'user' => $user,
        'access_token' => $user->createToken('google-token')->plainTextToken,
        'token_type' => 'Bearer',
    ]);
}

Next.js端代码

import React, { useState, useEffect } from 'react'
import { useRouter } from 'next/router'
function google() {
const Router = useRouter()
const [loading, setLoading] = useState(true)
const [data, setData] = useState({})
const [user, setUser] = useState(null)

// On page load, we take "search" parameters
// and proxy them to /api/auth/callback on our Laravel API
const p = Router.asPath
const q = p.split('?', 2)
const query = q[q.length - 1]
useEffect(() => {
    fetch(`http://localhost:8000/login/google/callback?${query}`, {
        headers: {
            'Content-Type': 'application/json',
            Accept: 'application/json',
        },
    })
        .then(response => {
            return response.json()
        })
        .then(data => {
            setLoading(false)
            setData(data)
        })
}, [])

useEffect(() => {
    window.sessionStorage.setItem('tb:user', JSON.stringify(data))
    console.log(JSON.stringify(data, null, 2))
})
// Helper method to fetch User data for authenticated user
// Watch out for "Authorization" header that is added to this call
function fetchUserData() {
    fetch(`http://localhost:8000/api/user`, {
        headers: {
            'Content-Type': 'application/json',
            Accept: 'application/json',
            Authorization: 'Bearer ' + data.access_token,
        },
    })
        .then(response => {
            return response.json()
        })
        .then(data => {
            setUser(data)
        })
}

if (loading) {
    return <DisplayLoading />
} else {
    if (user != null) {
        return <DisplayData data={user} />
    } else {
        return (
            <div>
                <DisplayData data={data} />
                <div style={{ marginTop: 10 }}>
                    <button onClick={fetchUserData}>Fetch User</button>
                </div>
            </div>
        )
    }
}
}

function DisplayLoading() {
return <div>Loading....</div>
}

function DisplayData(data) {
return (
    <div>
        <samp>{JSON.stringify(data, null, 2)}</samp>
    </div>
)
}

export default google
问题

已完成上述Google登录相关代码开发,但登录后无法获取用户信息,请问如何在Next.js中配置认证中间件以实现用户身份验证?


解决方案

1. 先修复现有代码的问题

你的Next.js代码存在两处影响功能的问题:

  • 第二个useEffect无依赖项,会在每次渲染时执行,可能在data未获取完成就往sessionStorage存入空值,修改如下:
useEffect(() => {
  if (data.access_token) {
    window.sessionStorage.setItem('tb:user', JSON.stringify(data))
    console.log(JSON.stringify(data, null, 2))
  }
}, [data])
  • 调用fetchUserData前需判断data.access_token是否存在,避免生成无效的Authorization请求头。

2. App Router版本(Next.js 13+)配置认证中间件

步骤1:创建中间件文件

在项目根目录新建middleware.ts:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // 从Cookie中读取token(中间件运行在服务端,无法访问sessionStorage)
  const token = request.cookies.get('access_token')?.value

  // 定义需要认证的路由
  const protectedRoutes = ['/dashboard', '/profile']
  const isProtectedRoute = protectedRoutes.some(route => request.nextUrl.pathname.startsWith(route))

  // 未登录且访问受保护路由时,跳转到登录页
  if (isProtectedRoute && !token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

// 指定中间件生效的路由范围
export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/api/protected/:path*'],
}

步骤2:登录成功后将Token存入Cookie

修改Next.js登录页的回调逻辑,把token同时存入Cookie(让中间件能读取到):

.then(data => {
  setLoading(false)
  setData(data)
  // 存入Cookie,有效期设为1天
  document.cookie = `access_token=${data.access_token}; max-age=86400; path=/; same-site=lax`
})

步骤3:封装带认证的请求函数

创建统一的请求工具,自动携带Token:

export const authFetch = async (url: string, options: RequestInit = {}) => {
  const token = document.cookie.split('; ').find(row => row.startsWith('access_token='))?.split('=')[1]
  const headers = {
    'Content-Type': 'application/json',
    Accept: 'application/json',
    ...options.headers,
  }
  if (token) {
    headers['Authorization'] = `Bearer ${token}`
  }
  return fetch(url, { ...options, headers })
}

之后用这个函数替代原生fetch,比如fetchUserData:

function fetchUserData() {
  authFetch(`http://localhost:8000/api/user`)
    .then(response => response.json())
    .then(data => setUser(data))
}

3. Pages Router版本配置认证中间件

如果使用旧版Pages Router,在pages/_middleware.ts中编写逻辑:

import type { NextFetchEvent, NextRequest } from 'next/server'
import { NextResponse } from 'next/server'

export function middleware(req: NextRequest, ev: NextFetchEvent) {
  const token = req.cookies.get('access_token')?.value
  const protectedPaths = ['/dashboard', '/profile']

  if (protectedPaths.includes(req.nextUrl.pathname) && !token) {
    return NextResponse.redirect('/login')
  }

  return NextResponse.next()
}

4. 简化方案:使用NextAuth.js

如果不想手动处理Token和中间件,可以用NextAuth.js集成Laravel认证:

  1. 安装依赖:npm install next-auth
  2. 创建pages/api/auth/[...nextauth].ts:
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import axios from 'axios'

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async signIn({ account, profile }) {
      // 将Google授权码传给Laravel后端获取Token
      const res = await axios.post('http://localhost:8000/login/google/callback', {
        code: account?.code,
      })
      return res.data.access_token ? true : false
    },
    async session({ session, token }) {
      // 将Token注入session,方便页面调用
      session.accessToken = token.accessToken
      return session
    },
  },
})

之后通过useSession钩子即可获取用户认证状态,NextAuth会自动处理路由拦截和Token管理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 00:54:33