这时就要请出托管身份验证提供程序 Clerk 了,它消除了身份验证中的所有难题,大大降低了妥善保护全栈应用程序的门槛。与其他托管身份验证提供程序相比,Clerk 的开发者体验也明显做得更好。
Desktop npx create-next-app@latest ✔ What is your project named? ... clerk-auth ✔ Would you like to use TypeScript with this project? ... No / Yes ✔ Would you like to use ESLint with this project? ... No / Yes ✔ Would you like to use Tailwind CSS with this project? ... No / Yes ✔ Would you like to use `src/` directory with this project? ... No / Yes ✔ Use App Router (recommended)? ... No / Yes ✔ Would you like to customize the default import alias? ... No / Yes现在我们切换到刚刚创建的 clerk-auth 文件夹,安装唯一的依赖项:Clerk。
npm install @clerk/nextjs接下来需要创建一个 Clerk 账户和新项目,获取要用到的 API 密钥。这就需要转到 clerk.com,创建一个账户,之后在仪表板上单击“Add application”。将应用程序命名为 clerk-auth-demo,并选择 Email + Google 的登录方式。如果需要,大家还可以添加其他登录方式。请放心,这不会对开发过程产生任何影响,Clerk 为替我们完成所有工作。
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_**** CLERK_SECRET_KEY=sk_test_******要完成 Clerk 身份验证配置,最后一步就是把这款身份验证提供程序添加到/src/app/layout.tsx 文件当中。如果大家比较熟悉传统的 Next.js 页面范式,会发现其内容跟/src/_app.tsx 文件差不多。
// 堆代码 duidaima.com import { ClerkProvider } from '@clerk/nextjs'; import './globals.css'; import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'] }); export const metadata = { title: 'Create Next App', description: 'Generated by create next app', }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <ClerkProvider appearance={{ variables: { colorPrimary: 'red', }, }} > <html lang="en"> <body className={inter.className}>{children}</body> </html> </ClerkProvider> ); }ClerkProvider 中提供一项 appearance 属性,我们可以在其中定制 Clerk 组件以匹配应用程序的设计风格。每个 Clerk 组件还能单独设置样式。到这一步,我们就能在应用程序中使用 Clerk 了。
export default function SignInPage() { return ( <div className="flex items-center justify-center h-screen"> <SignIn /> </div> ); }这里的文件路径可能跟大家习惯的传统 Next.js 应用有所区别,其中页面 URL 由/src/app/sign-in 文件夹来定义,代表着页面实际上位于/sign-in。中括号用于捕捉 Clerk 内部使用的/sign-in/... 之后的所有内容。使用新的 App Router 功能,页面本体将始终存放在 page.tsx 文件之内。至于/src/app/sign-up/[[..sign-up]]/page.tsx 注册页面,处理方式也基本相同:import { SignUp } from '@clerk/nextjs';
import { SignUp } from '@clerk/nextjs'; export default function SignUpPage() { return ( <div className="flex items-center justify-center h-screen"> <SignUp /> </div> ); }现在转到 http://localhost:3000/sign-in ,就能看到预制好的注册组件,它支持电子邮件、密码或者大家指定的任何社交身份验证提供程序。
import { UserButton } from '@clerk/nextjs'; export default function Home() { return ( <div className="flex justify-center items-center h-screen"> <div className="bg-white p-4 rounded-md flex items-center gap-4 text-gray-600"> <p>Hello, User!</p> <UserButton afterSignOutUrl="/" /> </div> </div> ); }这里我们使用到 Clerk 的 UserButton 组件。登录之后,它将为提供 User Setting 的下拉菜单,用户可以在其中更改密码、电子邮件地址和其他各种设置。这些功能是收费的,但毕竟能帮我们省下自行开发验证带来的时间和精力投入。
import { authMiddleware } from "@clerk/nextjs"; export default authMiddleware({ publicRoutes: ["/"] }); export const config = { matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"], };此外,将以下新变更添加到.env.local 文件当中,以告知 Clerk 在需要重新定向时如何操作。
// other settings NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/ NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/Clerk 提供的这个中间件,将确保只有 root 页面和注册/登录页面对未经身份验证的用户可见。现在让我们在/src/app/protected/page.tsx 上创建页面:
export default function Protected() { return ( <div className="flex justify-center items-center h-screen"> <div className="bg-white p-4 rounded-md flex items-center gap-4 text-gray-600"> <p>This is a protected page</p> </div> </div> ); }这样在登录和注销时,就会转向这个页面。请注意,如果未能通过身份验证,访问者将被重新定向至/sign-in。
import { UserButton, currentUser } from '@clerk/nextjs'; import Link from 'next/link'; export default async function Home() { const user = await currentUser(); return ( <div className="flex justify-center items-center h-screen"> <div className="bg-white p-4 rounded-md flex items-center gap-4 text-gray-600"> {user ? ( <> <p>Hello, User: {user.emailAddresses[0]?.emailAddress}</p> <UserButton afterSignOutUrl="/" /> </> ) : ( <Link href="/sign-in" className="text-blue-500"> Sign in </Link> )} </div> </div> ); }这是一个 React 服务器组件,会使用 await 从 Clerk 异步获取当前用户会话。取决于会话是否存在,它会显示 UserButton 以及用户的电子邮件地址,或者指向登录页面的链接。
import { auth } from '@clerk/nextjs'; import { NextResponse } from 'next/server'; export async function GET() { const { userId } = auth(); if (!userId) { return new Response('Unauthorized', { status: 401 }); } const data = { message: 'Hello User', id: userId }; return NextResponse.json({ data }); }这里的 auth()函数会检查是否存在 Clerk 会话。如果不存在,则抛出 401 未经授权错误。而如果用户成功通过了身份验证,接下来就是设置用户能在端点上进行的操作了。我们可以访问 userId,据此将数据库中的数据引用给用户。