掌握Next.js(七)– 身份验证

​本文学习在Next.js项目中进行身份验证。内容包括设置Next Auth, 配置Google身份验证,身份认证的会话,路径的保护,身份信息的数据库保存,以及配置身份认证允许邮件和密码登录。


设置Next Auth

我们选择Next Auth用于身份验证,这是非常流行的库,官网地址是:

https://next-auth.js.org

安装:

npm install next-auth

再创建路径:/auth/[…nextauth]/route.tsx。

在官网复制一段代码,主要工作是将GET, POST等方法公布出去并由handler处理。

接着设置两个环境变量NEXTAUTH_URL和NEXTAUTH_SECRET,其中NEXTAUTH_SECRET用于认证信息的加密和签名,可以是任意字符串,我们随机生成了32位的BASE64编码,可以用openssl生成:

openssl rand -base64 32


配置谷歌身份验证提供程序

Next.js有一个概念叫身份验证提供程序,它是可用于登录用户的服务。

有很多身份验证提供程序,比如github, apple, twitter, facebook等,先配置google。

查看说明:https://next-auth.js.org/providers/google。

谷歌的配置页是:

https://console.developers.google.com/apis/credentials

大概过程是:

  • 创建项目,名称:My Next App配置”同意屏幕”。类型选”外部”。应用名称 My Next App,并设置电子邮件信息,其它保持空。
  • 添加要求用户授权的”范围”,选email和profile。
  • 添加测试用户,输入一个gmail邮件地址。
  • 再创建凭据。选择创建OAuth客户端ID,应用类型选”WEB应用”,名称”My Next App”,接着设定两个URI: 已获授权的Javascript来源设置url为”http://localhost:3000″;已获授权的重定向URI设置为 “http://localhost:3000/api/auth/callback/google”。

然后我们会得到客户端ID和客户端密钥,这个类似是与google会话的用户密码。保存下来。

配置 .env文件,添加GOOGLE_CLIENT_ID和GOOGLE_CLIENT_SECRET两个变量。

接着,我们要在项目中建一个谷歌认证提供程序,官网有示例,可以复制过来,并放在api/auth/[…nextauth]/route.tsx中。主要关联谷歌凭证验证。

最后一步,在NavBar添加链接。

考虑到国内实际情况,谷歌身份证验提供程序大多数人用不了,我们再来添加github验证提供程序,过程差不多。

访问 https://github.com/settings/apps 来配置OAuth。配置重定向URI及获取用户和密码等。

同样的,需要在 .env 添加 github 变量。

再添加GitHubProvider。

添加完成Provider后,我们测试一下。访问 http://localhost:3000/api/auth/signin

点击 “Sign in with Github”或”Sign in with Google”会弹出授权界面,授权后返回主页面,说明已通过账户验证。

验证后,我们并没有获取账户信息,后面会实现。


理解身份验证的session

身份认证session是个重要概念,我们必须要理解。

当用户登录后,NextAuth为该用户创建session,默认情况下,这个session使用”Json Web Token”(JWT)。

打开谷歌开发工具-应用-Cookie,能看到有一个next-auth.session-token,它的内容实际上就是Json Web Token。

我们查看一下jwt的内容,新建 /api/auth/token目录,再新建route.tsx(实际项目永远不必这么做,这里仅作演示)。

用户登录后并获取session后,下次会将session发往服务器用于证明自己,我们使用getToken获取请求的token。

访问 /api/auth/token,很明显,我们看到session就是Json Web Token。默认情况下,JWT的有效时间为30天((exp-iat)/3600/24),其中的sub类似于用户ID。


在客户端获取Session

Next.js有一个SessionProvider组件,它是React Context,用于在组件间传递用户Session信息。在app目录下新建 auth目录,再新建Provider.tsx文件,并创建AuthProvider组件。

该组件主要用于将子组件用SessionProvider包起来,使子组件有用户Session。由于用于状态传递,该组件要声明 “use client”;

再到layout.tsx,调用AuthProvider组件。

这样,layout下的组件都会有session信息,我们到NavBar.tsx修改。

首先使用useSession获取Session,当session的状态是 ‘loading’时,显示 loading; 当状态是’unauthenticate’时,显示登录链接; 当状态是’authenticated’时,显示用户姓名。另外,NavBar也需要改成客户端组件。


在服务器端获取Session

获取服务器session使用 getServerSession,调用该函数需要用到一些认证参数,我们在/app/api/auth/[…nextauth]/route.tsx提取authOptions。

再到主页用getServerSession在服务器端获取session并显示。

查看页面,session信息已获取到了。


注销用户

注销用户只需访问 NextAuth的端点 /api/auth/Signout即可。

访问该链接会有一个提示,这个提示也是可以自定义的。点”Sign out”后用户会退出登录。再用谷歌开发者工具查看cookie发现session已删除。


保护路径

保护路径,需要创建中间件来实现,中间件在请求完成之前执行,这样我们就可以执行比如判断用户session是否正确,不正确的话就重定向到登录界面的操作等。

在项目的根目录(不是app目录下),创建middleware.ts文件,注意拼写不能错,这是约定的。然后创建函数 middleware,同样的该函数名也不能拼错。在middleware函数中,我们可以做相关判断及重定向,作为演示,这里就直接进行重定向到 /new-page。配置之后,我们访问任何路径都会转向/new-page,当然,该路径是不存的。

我们可以定义且导出一个常数config, 这也是约定的不能拼错。matcher属性可以跟字符串或字符串数组。参数的表示中的 * 表示0至多个,+ 表示 1至1多个,?表示 0至1个。我们这里 /users/:id*,表示参数是0至多个,这样就匹配 /users 、 /users/1 、 /users/1/3 等路径。

设置了config后,只有匹配的路径才执行 middleware函数。

回到页面,我们访问 http://localhost:3000,不会被重定向;而如果访问http://localhost:3000/users时则会被重定向,因为路径匹配config的matcher。

NextAuth自带一个middleware函数,它的作用是是检查用户的session,如果session不存在,且路径匹配config的matcher,就跳转到登陆页面。我们修改下。

这里的导入和导出两句可合成一句:

export {default} from “next-auth/middleware”;


数据库适配器

github验证账户登录后,在现实项目中,还是需要将用户的数据存数据库的。查看next-auth文档,找到adapter这一节。

https://next-auth.js.org/adapters

我们用的ORM是Prisma,对照说明,首先安装prisma-adapter。

npm install @prisma/client @auth/prisma-adapter

因adapter有个模型也是User,先删除原先建的User和Product模型,并进行迁移:npx prisma migrate dev。然后复制文档的4个模型 Account、Session、User、VerificationToken,修改完成的schema.prisma如下:

再进行一次迁移。

npx prisma migrate dev

修改/api/auth/[…nextauth]/route.tsx,添加属性adapter并设置为PrismaAdapter,添加属性 session,其下的属性strategy设置为 “jwt”,因为目前github的session就是JWT。

再访问 /api/auth/signin,并登录github。

再查看一下数据库,发现User和Account已添加了一条用户记录。


配置凭据提供程序

用google, github等OAuth验证提供程序很方便,我们不用做太多工作,只需询问google和github账户是否合法,然后就可取到用户信息,不用自己考虑密码及加密存储等一系列问题。不过有些时候,还是需要自己处理的,我们来看看如何做。

参照官网说明:https://next-auth.js.org/providers/credentials,配置CredentialsProvider。

使用email和password进行验证,其中password在User模型里是没有的,需要添加。导入bcrypt进行password的加密比较:import bcrypt from “bcrypt”。

安装bcrypt。
npm i bcryptnpm i -D @types/bcrypt

添加User模型字段hashedPassword。

到浏览器验证下。

当然,我们输入电子邮件和密码是不能登录的,因为数据库还没有相关账户信息,下一节实现。


注册用户

注册用户与之前介绍Prisma时的创建对象类似,使用POST方法。

在api目录下新建register目录,再创建route.tsx进行编码,大致步骤是:

首先获取请求体,用zod验证email和password的数据格式及长度是否符合要求,不符合的返回400错误。

接着,在数据库查找是否存在请求的email, 如果存在,告知已存在不能注册。

用bcrypt加密password,然后在数据库里创建一个用户,并返回创建的用户。

在Postman测试POST成功。

完成之后,再访问 /api/auth/signin,用刚注册的email和密码就可以登录了。


小结

本文介绍了Next.js使用Next-Auth进行身份验证的相关知识。

下一篇介绍电子邮件的发送。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注