Next.js项目实践(六)– 用户验证

本文实现用户验证。


设置Next Auth

安装:npm i next-auth@4.23.1

在/app/api下新建auth/[…nextauth]目录,再新建route.tsx文件。

// /app/api/auth/[...nextauth]/route.tsx
​
import NextAuth from "next-auth";
​
const handler = NextAuth({
  providers: [],
});
​
export { handler as GET, handler as POST };
​

再在 .env添加NEXTAUTH_URL和NEXTAUTH_SECRET,其中NEXTAUTH_SECRET由openssl生成:openssl rand -base64 32

// .env
​
...
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="ngGYFDDXYGNA2dwG+9IVC+klO+0OYxGlViovNGc4aBk="

提交Git,命名:Set up NextAuth。


配置Github提供者

访问:https://github.com/settings/apps,添加一个OAuth应用,获取Client ID和Client Secrets。并在 .env里配置进去。

// .env
​
...
GITHUB_ID="aaf751407814c4719fbd"
GITHUB_SECRET="091a5f24badf9513bb7525cb72ce17972200e09e"

再在app/api/auth/[…nextauth]/route.tsx添加Github提供者信息。

提交Git,命名:Configure Github Provider。


添加 Prisma 适配器

这个在《掌握Next.js》系列中已讲过,主要步骤参考官网。

https://authjs.dev/reference/adapter/prisma

  1. 安装prisma-adapter:npm i @auth/prisma-adapter
  2. 创建四个model: Account,Session,User,VerificationToken
  3. 迁移:npx prisma migrate dev

完成后,访问 /api/auth/signin,用github账户登录,成功后返回首页,同时检查数据库的User、Account表已有记录。

提交Git,命名:Add the Prisma adapter。


添加登录和注销链接

需要用到useSession的Hook, 需要添加SessionProvider, 《掌握Next.js》里已有细讲,这里只给出结果。

添加目录 /app/auth/, 添加Provider.tsx

// app/auth/Provider.tsx
​
"use client";
import { SessionProvider } from "next-auth/react";
import { PropsWithChildren } from "react";
​
const AuthProvider = ({ children }: PropsWithChildren) => {
  return <SessionProvider>{children}</SessionProvider>;
};
​
export default AuthProvider;

再到layout.tsx用AuthProvider将所有子组件包起来。

再到NavBar添加登录注销链接。

查看网页。

提交Git,命名:Add the Login and Logout links。


更改导航栏的布局

我们把账户的登录状态显示在NavBar的右侧。调整NavBar的样式。将左侧与右侧的账户登录信息用Flex包起来,使用 justify=’between’,再在外面包一层Container,免得屏幕很宽时太靠近左右侧。

查看页面。

提交Git,命名:Change the layout of the navbar。


添加下拉菜单

Radix-UI的DropdownMenu很容易使用。

网页效果。

提交Git,命名:Add a drop-down menu to show the current user。


故障排除:头像未加载

从Google等提供者获取信息时,可能会遇到头像不显示的情况,可以尝试以下方法。

  1. 添加Avatar属性 referrerPolicy=”no-referrer”

2.修改next.config.js,添加HTTP头相关信息。

/** @type {import('next').NextConfig} */
const nextConfig = {
    async headers(){
        return [
            {
                source: '/:path*',
                headers: [
                    {key:'referrer-policy', value:'no-referrer'}
                ]
            }
        ]
    }
}
​
​
module.exports = nextConfig
​

提交Git,命名:Fix avatar not loading.


重构导航栏

NavBar布局和每个布局块均放在一起,不太符合单一责任原则,需要重构。有两种选择,一种像之前一样将细节分离到另外文件,另一种则是将子组件放在当前文件里。这两种选择没有对错,本次就放在当前文件里实现。

另外为保持AuthStatus和NavLinks样式一致,抽了一个样式形成 nav-link,设置在global.css中。

// app/global.css
​
......
@layer utilities {
  .nav-link {
    @apply text-zinc-500 hover:text-zinc-800 transition-colors
  }
}
​

提交Git,命名:Refactor the nav bar.


添加加载Skeleton

给AuthStatus组件添加Skeleton,否则会有闪烁看上去比较奇怪。

提交Git,命名:Add a loading skeleton.


保护应用程序

创建、删除、编辑等操作是需要保护的,未验证的用户不能操作。利用next-auth自带的middleware就可做到。

在项目的根目录(不是app目录下),创建middleware.ts文件,注意拼写不能错,这是约定的。设置 /issues/new 和 /issues/:id+/edit 必须要登录才能操作。:id+ 表示参数1个至多个,《掌握Next.js》系列已有介绍,这里不赘述。在未登录状态下,测试访问新建和编辑页面,均会弹出窗口要求用户验证。

// middleware.ts
​
export {default} from 'next-auth/middleware';
​
export const config = {
    matcher:[
        '/issues/new',
        '/issues/:id+/edit'
    ]
}


删除如何保护?它是没有具体URL的。
我们应该在IssueDetailPage就判断是否登录,如果没登录,就不显示按钮。这就用到服务器session的获取,这在《掌握Next.js》系列里也介绍过。
提取出authOptions, 形成文件 /app/auth/authOptions.ts.

// /app/auth/authOptions.ts
​
import prisma from "@/prisma/client"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { AuthOptions } from "next-auth";
import GitHubProvider from "next-auth/providers/github";
​
const authOptions:AuthOptions = {
    adapter: PrismaAdapter(prisma),
    providers: [
      GitHubProvider({
        clientId: process.env.GITHUB_ID!,
        clientSecret: process.env.GITHUB_SECRET!,
      }),
    ],
    session: {
      strategy: "jwt",
    },
  };
​
export default authOptions;

在app/issues/[id]/page.tsx里判断session,不存在就不显示编辑和删除按钮。

相应API端点也进行session的判断。

提交Git,命名:Secure the application.


小结

本文实现了用户验证功能。

下一篇实现分配问题到用户的功能。

发表评论

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