2025上半年,最新 AI实践都在这!20+ 应用案例,任听一场议题就值回票价 了解详情
写点什么

如何在 Next.js 全栈应用程序中无缝实现身份验证

作者:Zevi Reinitz

  • 2023-08-21
    北京
  • 本文字数:4456 字

    阅读完需:约 15 分钟

如何在Next.js全栈应用程序中无缝实现身份验证

本教程中,我们将一同了解如何使用 Clerk 向全栈应用程序添加身份验证机制。

 

我们跟 Clerk 没有任何合作关系,但对这款工具的表现非常认可。很多朋友正好咨询怎么在 Next.js 下实现身份验证,这篇文章专为解决问题而来。

 

背景介绍

 

身份验证一直是构建全栈应用程序中的一大主要痛点。特别是在 Node.js 环境当中,各种主流库和框架都没有内置 auth-primitives。因此,开发人员不得不自己想办法构建身份验证解决方案。

 

但从零开始构建安全身份验证是项颇为艰巨的任务。我们首先得对密码进行哈希和加盐处理,发布签名令牌来创建会话,同时防止各种恶意攻击向量。此外,大家还得保证自己的前端和后端能够相互通信、正常共享会话。

 

好消息是,Express 的 Passport.js 和 Next.js 的 NextAuth 等库就是为此而生,只是还不够完美。这些库的设置流程涉及多个步骤,虽然已经能较好地配合 Google 或 GitHub 等服务实现社交身份验证,但毕竟要比密码登录更困难。而且密码内容仍须存储在服务端数据库内,由软件开发一方承担全部安全责任。

 

如今,登录时通过邮件验证、无密码登录和双因素身份验证已经相当流行。虽然前面讨论的库也能支持这些功能,但需要在本就复杂的设置之外再做更多额外工作。

这时就要请出托管身份验证提供程序 Clerk 了,它消除了身份验证中的所有难题,大大降低了妥善保护全栈应用程序的门槛。与其他托管身份验证提供程序相比,Clerk 的开发者体验也明显做得更好。

 

在本教程中,我们将运用 Clerk 及其全新 App Router,在 Next.js 13 当中构建一款简单的全栈应用程序。



设置


首先在您终端中指定的文件夹中运行命令 npx create-next-app@latest,从而创建新的 Next 应用程序。请按以下指定方式完成设置。需要注意的是,一定要在 Tailwind CSS 和 App Router 部分选择 Yes。

 

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 为替我们完成所有工作。



 现在,Clerk 会自动提供要添加到 Next 应用程序的 API 密钥。



 因此,请创建一个.env.local 文件,把 Clerk 那边复制的内容全部粘贴进来。

 

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_****CLERK_SECRET_KEY=sk_test_******
复制代码

 

要完成 Clerk 身份验证配置,最后一步就是把这款身份验证提供程序添加到/src/app/layout.tsx 文件当中。如果大家比较熟悉传统的 Next.js 页面范式,会发现其内容跟/src/_app.tsx 文件差不多。

 

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 了。

 

向应用添加身份验证

登录和注册页


首先,我们需要创建注册和登录页。Clerk 已经提供了完整的表单组件,剩下要做的就是利用这些组件构建一个简单的示例页面。

 

我们从登录页开始。使用以下内容,在/src/app/sign-in/[[..sign-in]]/page.tsx 中创建一个新组件:import { SignIn } from '@clerk/nextjs';

 

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 ,就能看到预制好的注册组件,它支持电子邮件、密码或者大家指定的任何社交身份验证提供程序。



账户页面


创建一个账户,或者通过 Google 进行登录。到这里,我们已经完成了应用登录,但目前的功能还比较有限。所以我们需要创建账户页面,首先将/src/app/page.tsx 文件中的内容变更为:

 

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 的下拉菜单,用户可以在其中更改密码、电子邮件地址和其他各种设置。这些功能是收费的,但毕竟能帮我们省下自行开发验证带来的时间和精力投入。

 

保护页面

Secret 页面

 

现在我们需要在/protectet 上创建一个新页面,要求该页面只能由经过身份验证的用户访问。为此,我们需要在/src/middleware.ts 中创建一个新的中间件,内容如下:

 

import { authMiddleware } from "@clerk/nextjs";export default authMiddleware({  publicRoutes: ["/"]});export const config = {  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],};
复制代码

 

此外,将以下新变更添加到.env.local 文件当中,以告知 Clerk 在需要重新定向时如何操作。

 

// other settingsNEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-inNEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-upNEXT_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。

 

在主页中显示登录链接

 

当用户尚未登录时,我们的 root 页面目前不会显示任何信息。但现在中间件已经设置完毕,我们可以修改/src/app/page.tsx 文件来更改此中间件:

 

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 以及用户的电子邮件地址,或者指向登录页面的链接。

 

保护 API 路由


到这里,我们已经讨论了如何保护应用前端。但全栈应用程序还有后端部分,为此我们将在新的 App Router 模式中使用/src/app/api/route.ts 文件,借此在 GET/api 处创建一个后端端点:

 

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,据此将数据库中的数据引用给用户。

 

总结


至此,我们已经在全栈 Next.js 13 应用程序中完成了 Clerk Authentication 的完整实施。可以看到,整个过程几乎无需编写任何身份验证代码就能正常起效!这也是 Clerk 等外部提供程序的魅力所在。更重要的是,我们的小小演示应用也内置了一系列用户管理功能,包括验证/更改电子邮件地址、更改密码和社交登录等,能帮开发者省下很多时间。

 

对于同时拥有前端和后端的全栈应用程序,Clerk 在 Next.js 等框架中有着相当出彩的表现。但如果匹配单独的后端,那在设置方面就要更复杂一些。Clerk 可以发出 JWT 令牌,由开发者将其与 API 请求一同发往后端以验证用户身份。这种方式虽然可行,但整个过程肯定不如本文展示的那样无缝丝滑。

 

原文链接:


https://dev.to/livecycle/seamless-full-stack-authentication-in-nextjs-11lp


相关阅读:


为什么说 Next.js 13 是一个颠覆性版本

Next.js 实践:从 SSR 到 CSR 的优雅降级

Next.js 13 新的实验性特性,实现 App“动态无限制”

我们如何使用 Next.js 将 React 加载时间缩短 70%

2023-08-21 11:473843

评论

发布
暂无评论
发现更多内容

架构师训练营第十二周总结

Hanson

你真的知道什么是线程安全吗?

老胡爱分享

并发编程 线程 java 并发

听说,阿里云给它的 OpenAPI 开发了一套编程语言

郭旭东

阿里云 OpenAPI

Atlassian Team Playbook | 用户体验画布挖掘业务价值

Atlassian

团队管理 敏捷开发 Atlassian

【Spring Boot系列】之多种依赖集成方式

语霖

大数据解答(一)

dony.zhang

大数据 mapreduce

cookie和session的关系看这一篇就够了

架构师修行之路

session Cookie

甲方日常 5

句子

工作 随笔杂谈 日常

所按非所得——聊一聊StandHogg漏洞

OPPO安全

安全攻防 安全 安全开发 漏洞

架构设计开学第一天

escray

学习 从零开始学架构 架构师预科班

kubernetes node affinity 写法

Geek_f24c45

Kubernetes

2020-08-29-第十二周作业

路易斯李李李

2020-08-29-第十二周学习总结

路易斯李李李

AtlassianTeam Playbook | 用户体验中的移情地图

Atlassian

团队管理 敏捷开发 Atlassian

oeasy教您玩转linux010201持续输出yes

o

3种 Springboot 全局时间格式化方式,别再写重复代码了

程序员小富

Java springboot

云算力挖矿平台APP,算力挖矿建设开发

13530558032

42图揭秘,「后端技术学些啥」

我是程序员小贱

Week 12 命题作业

Jeremy

Week 12 学习总结

Jeremy

互联网人必备知识cookie和session认证

架构师修行之路

分布式 身份认证 session Cookie

Flink SQL FileSystem Connector 分区提交与自定义小文件合并策略

Apache Flink

flink

第九周

Acker飏

我所在公司和大数据

2流程序员

大数据应用场景

dongge

架构师训练营第十二周作业

Hanson

微服务架构下的系统集成

码猿外

架构 微服务 系统集成

架构师训练营 -- 第 12 周作业

stardust20

Docker 私有镜像仓库的搭建及认证

哈喽沃德先生

Docker 容器 微服务 镜像

计算机网络基础(二十二)---传输层-套接字与套接字编程

书旅

TCP 计算机网络 TCP/IP

FlinkX 如何读取和写入 Clickhouse?

Apache Flink

flink

如何在Next.js全栈应用程序中无缝实现身份验证_工程化_InfoQ精选文章