Skip to content

Latest commit

 

History

History
385 lines (305 loc) · 7.85 KB

File metadata and controls

385 lines (305 loc) · 7.85 KB

Google 登录使用示例

🎯 基础用法

1. 在客户端组件中使用

"use client"

import { useSession } from "next-auth/react"
import { GoogleSignIn } from "@/components/GoogleSignIn"

export default function MyPage() {
  const { data: session, status } = useSession()

  if (status === "loading") {
    return <div>加载中...</div>
  }

  return (
    <div>
      {session ? (
        <div>
          <h1>欢迎, {session.user?.name}!</h1>
          <p>邮箱: {session.user?.email}</p>
          {session.user?.image && (
            <img src={session.user.image} alt="头像" />
          )}
        </div>
      ) : (
        <div>
          <h1>请登录</h1>
          <GoogleSignIn />
        </div>
      )}
    </div>
  )
}

2. 在服务端组件中使用

import { auth } from "@/lib/auth"
import { redirect } from "next/navigation"

export default async function ProfilePage() {
  const session = await auth()

  if (!session) {
    redirect("/")
  }

  return (
    <div>
      <h1>个人资料</h1>
      <p>姓名: {session.user?.name}</p>
      <p>邮箱: {session.user?.email}</p>
    </div>
  )
}

3. 保护整个页面

"use client"

import { ProtectedRoute } from "@/components/ProtectedRoute"

export default function DashboardPage() {
  return (
    <ProtectedRoute>
      <div>
        <h1>仪表板</h1>
        <p>这是受保护的内容,只有登录用户才能看到</p>
      </div>
    </ProtectedRoute>
  )
}

4. 自定义登录按钮

"use client"

import { signIn } from "next-auth/react"

export function CustomSignInButton() {
  return (
    <button
      onClick={() => signIn("google", { callbackUrl: "/dashboard" })}
      className="your-custom-styles"
    >
      使用 Google 登录
    </button>
  )
}

5. 自定义登出按钮

"use client"

import { signOut } from "next-auth/react"

export function CustomSignOutButton() {
  return (
    <button
      onClick={() => signOut({ callbackUrl: "/" })}
      className="your-custom-styles"
    >
      退出登录
    </button>
  )
}

🔐 路由保护

使用中间件保护多个路由

创建 middleware.ts 文件:

export { auth as middleware } from "@/lib/auth"

// 配置需要保护的路由
export const config = {
  matcher: [
    "/dashboard/:path*",
    "/profile/:path*",
    "/settings/:path*",
  ],
}

条件性路由保护

import { auth } from "@/lib/auth"
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

export async function middleware(request: NextRequest) {
  const session = await auth()
  const isProtectedRoute = request.nextUrl.pathname.startsWith("/dashboard")

  if (isProtectedRoute && !session) {
    return NextResponse.redirect(new URL("/", request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}

🎨 UI 组件示例

用户头像下拉菜单

"use client"

import { useSession, signOut } from "next-auth/react"
import { useState } from "react"

export function UserMenu() {
  const { data: session } = useSession()
  const [isOpen, setIsOpen] = useState(false)

  if (!session) return null

  return (
    <div className="relative">
      <button
        onClick={() => setIsOpen(!isOpen)}
        className="flex items-center gap-2"
      >
        <img
          src={session.user?.image || "/default-avatar.png"}
          alt={session.user?.name || "User"}
          className="w-8 h-8 rounded-full"
        />
        <span>{session.user?.name}</span>
      </button>

      {isOpen && (
        <div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg">
          <div className="p-4 border-b">
            <p className="font-medium">{session.user?.name}</p>
            <p className="text-sm text-gray-500">{session.user?.email}</p>
          </div>
          <button
            onClick={() => signOut()}
            className="w-full text-left px-4 py-2 hover:bg-gray-100"
          >
            退出登录
          </button>
        </div>
      )}
    </div>
  )
}

登录状态指示器

"use client"

import { useSession } from "next-auth/react"

export function AuthStatus() {
  const { data: session, status } = useSession()

  if (status === "loading") {
    return <span className="text-gray-500">检查登录状态...</span>
  }

  if (session) {
    return (
      <span className="text-green-600">
        ✓ 已登录为 {session.user?.name}
      </span>
    )
  }

  return <span className="text-gray-500">未登录</span>
}

🔄 API 路由中使用

保护 API 端点

// app/api/protected/route.ts
import { auth } from "@/lib/auth"
import { NextResponse } from "next/server"

export async function GET() {
  const session = await auth()

  if (!session) {
    return NextResponse.json(
      { error: "未授权" },
      { status: 401 }
    )
  }

  return NextResponse.json({
    message: "这是受保护的数据",
    user: session.user,
  })
}

获取当前用户信息的 API

// app/api/me/route.ts
import { auth } from "@/lib/auth"
import { NextResponse } from "next/server"

export async function GET() {
  const session = await auth()

  if (!session) {
    return NextResponse.json(
      { error: "未登录" },
      { status: 401 }
    )
  }

  return NextResponse.json({
    user: {
      id: session.user?.id,
      name: session.user?.name,
      email: session.user?.email,
      image: session.user?.image,
    },
  })
}

📱 响应式登录按钮

"use client"

import { useSession, signIn, signOut } from "next-auth/react"

export function ResponsiveAuthButton() {
  const { data: session, status } = useSession()

  if (status === "loading") {
    return (
      <div className="w-8 h-8 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin" />
    )
  }

  if (session) {
    return (
      <div className="flex items-center gap-2">
        {/* 桌面端显示完整信息 */}
        <div className="hidden md:flex items-center gap-2">
          <img
            src={session.user?.image || "/default-avatar.png"}
            alt={session.user?.name || "User"}
            className="w-8 h-8 rounded-full"
          />
          <span className="text-sm">{session.user?.name}</span>
        </div>
        
        {/* 移动端只显示头像 */}
        <img
          src={session.user?.image || "/default-avatar.png"}
          alt={session.user?.name || "User"}
          className="w-8 h-8 rounded-full md:hidden"
        />
        
        <button
          onClick={() => signOut()}
          className="px-3 py-1 text-sm bg-red-500 text-white rounded hover:bg-red-600"
        >
          退出
        </button>
      </div>
    )
  }

  return (
    <button
      onClick={() => signIn("google")}
      className="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 flex items-center gap-2"
    >
      <svg className="w-5 h-5" viewBox="0 0 24 24">
        {/* Google 图标 SVG */}
      </svg>
      <span className="hidden sm:inline">使用 Google 登录</span>
      <span className="sm:hidden">登录</span>
    </button>
  )
}

🎯 实用技巧

1. 登录后重定向到特定页面

signIn("google", { callbackUrl: "/dashboard" })

2. 登出后重定向

signOut({ callbackUrl: "/" })

3. 检查用户是否有特定邮箱域名

const { data: session } = useSession()
const isCompanyEmail = session?.user?.email?.endsWith("@company.com")

4. 在 useEffect 中处理登录状态

useEffect(() => {
  if (status === "authenticated") {
    // 用户已登录,执行某些操作
    console.log("用户已登录:", session.user)
  }
}, [status, session])

这些示例涵盖了大多数常见的使用场景。根据你的具体需求,可以自由组合和修改这些代码!