【AICon】AI 基础设施、LLM运维、大模型训练与推理,一场会议,全方位涵盖! >>> 了解详情
写点什么

如何在 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:472369

评论

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

阿里云刘强:无影云电脑构建云上安全办公室

阿里云弹性计算

弹性计算 年度峰会 无影云电脑

[转]注释驱动的 Spring cache 缓存介绍

kimmking

译文丨伯克利对serverless的看法:简化云编程

华为云开发者联盟

Serverless 云编程 伯克利 无服务器计算 云函数

前端开发代码区域规范分享

@零度

前端开发 代码规范

Towhee,开源的 embedding 框架与社区

Zilliz

数据库 开源 向量检索

如何使用JDBC API操作数据库

编程江湖

JDBC

大数据开发之Spark Shuffle 原理分析

@零度

大数据 spark

vscode中Tasks及Emmet的应用

编程江湖

vscode

Avue复选框动态赋值不能渲染问题解决方式

泉城老铁

前端 avue

尚硅谷Docker与微服务实战教程发布

@零度

大数据 dokcer

开源走向世界(上):开源构建全球化的舞台丨BDTC 2021

PingCAP

运维工程师必备利器|一招实现运维智能化!

云智慧AIOps社区

运维 AIOPS 运维工程师 基础知识 自动化运维

java开发之内存模型面试分享

@零度

JAVA开发 Java内存模型

数据分析人员需要掌握SQL到什么程度?3个常考题目刷一刷

博文视点Broadview

Avue中如何对option中属性动态赋值

泉城老铁

前端 avue

据说有人面试栽在了Thread类的stop()方法和interrupt()方法上

华为云开发者联盟

高并发 crud Thread类 stop interrupt

湖仓一体天花板,大数据一站式SQL分析技术实践

华为云开发者联盟

大数据 HetuEngine 湖仓一体 SQL分析 华为云FusionInsight

web技术分享| web的白板工具栏封装

anyRTC开发者

前端 Web 音视频 视频会议 白板

java学习中cookie原理

编程江湖

java 编程

工作中遇到的50个JavaScript的基础知识点

Sunshine_Lin

面试 前端 进阶 基础

ADmobile首席架构师王威:广告业务云上运维最佳实践

阿里云弹性计算

阿里云 弹性计算 年度峰会

一文带你看懂分布式软总线在家庭场景的应用

HarmonyOS开发者

HarmonyOS

自动驾驶训练如火如荼,网络带宽跟不上怎么破?

焱融科技

人工智能 自动驾驶 云计算 高性能 文件存储

深入解析QUIC协议

拍乐云Pano

WebRTC RTC QUIC QUIC协议

在线JSON转XML工具

入门小站

工具

网络安全好学吗?手把手教你学metasploit信息收集 网络安全工程师学习资料汇总

学神来啦

利用闭包实现自定义等待方法

FunTester

多线程 并发测试 闭包 FunTester 自定义等待

Apache Oozie学习笔记(一)

恒生LIGHT云社区

大数据 hadoop 工作流 调度

CI/CD制作流程

wong

Docker jenkins ansible kubenetes

Linux之文件属性详解

入门小站

Linux

元宇宙到家,那些「聪明」的设计工具

Zilliz

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