原文链接
CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧
上一篇【Next.js 项目实战系列】05-删除 Issue
身份验证
配置 Next-Auth
本节代码链接
具体内容可参考Authentication
Refactor NavBar
本节代码链接
# /app/Navbar.tsx"use client";
import { Avatar, Box, DropdownMenu, Flex, Text } from "@radix-ui/themes";
import classNames from "classnames";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { AiFillBug } from "react-icons/ai";
import { Skeleton } from "@/app/components";const NavBar = () => {return (<nav className="border-b mb-5 px-5 py-5"><Flex align="center" justify="between"><NavLinks /><Avator /></Flex></nav>);
};export default NavBar;const links = [{ label: <AiFillBug />, href: "/" },{ label: "DashBoard", href: "/dashboard" },{ label: "Issues", href: "/issues" },
];const NavLinks = () => {const currentPath = usePathname();return (<ul className="flex gap-6 items-center">{links.map((link) => (<li key={link.href}><LinkclassName={classNames({"text-zinc-900": link.href === currentPath,"text-zinc-500": link.href !== currentPath,"hover:text-zinc-800 transaition-colors": true,})}href={link.href}>{link.label}</Link></li>))}</ul>);
};const Avator = () => {const { status, data: session } = useSession();if (status === "loading") return <Skeleton width="3rem" />;if (status === "unauthenticated")return (<LinkclassName="text-zinc-500 hover:text-zinc-800 transaition-colors"href="/api/auth/signin">Sign In</Link>);return (<Box><DropdownMenu.Root><DropdownMenu.Trigger><Avatarsrc={session!.user!.image!}fallback="?"size="2"radius="full"className="cursor-pointer"referrerPolicy="no-referrer"/></DropdownMenu.Trigger><DropdownMenu.Content><DropdownMenu.Label><Text size="2">{session!.user!.email}</Text></DropdownMenu.Label><DropdownMenu.Item><Link href="/api/auth/signout">Sign Out</Link></DropdownMenu.Item></DropdownMenu.Content></DropdownMenu.Root></Box>);
};
Secure the Application
本节代码链接
AuthOptions
首先,我们应该将 AuthOptions 放到一个单独的文件里备用
# /app/api/auth/AuthOptions.tsximport prisma from "@/prisma/client";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { NextAuthOptions } from "next-auth";
import Github from "next-auth/providers/github";const authOptions: NextAuthOptions = {adapter: PrismaAdapter(prisma),providers: [Github({clientId: process.env.GITHUB_CLIENT_ID!,clientSecret: process.env.GITHUB_CLIENT_SECRET!,}),],
};export default authOptions;
保护 API 与 页面
在除 GET
外的所有 API 中都应该加上确认是否有 session 的验证
# /app/api/issues/[id]/route.tsx...
+ import { getServerSession } from "next-auth";
+ import authOptions from "@/app/api/auth/AuthOptions";export async function PATCH(request: NextRequest,{ params }: { params: { id: string } }) {
+ const session = await getServerSession(authOptions);
+ if (!session) return NextResponse.json({}, { status: 401 });...}export async function DELETE(request: NextRequest,{ params }: { params: { id: string } }) {
+ const session = await getServerSession(authOptions);
+ if (!session) return NextResponse.json({}, { status: 401 })...}
同样,我们应该在一些页面将删除和修改的组件隐藏
# /app/issues/[id]/page.tsx+ import { getServerSession } from "next-auth";
+ import authOptions from "@/app/api/auth/AuthOptions";interface Props {params: { id: string };}const IssueDeatilPage = async ({ params }: Props) => {
+ const session = await getServerSession(authOptions);const issue = await prisma.issue.findUnique({where: { id: parseInt(params.id) },});if (!issue) notFound();return (<Grid columns={{ initial: "1", sm: "5" }} gap="5"><Box className="md:col-span-4"><IssueDetails issue={issue} /></Box>
+ {session && (<Box><Flex direction="column" gap="3"><EditIssueButton issueId={issue.id} /><DeleteIssueButton issueId={issue.id} /></Flex></Box>)}</Grid>);};export default IssueDeatilPage;
MiddleWare
同样我们可以使用 MiddleWare 来保护路由
export { default } from "next-auth/middleware";export const config = {matcher: ["/issues/new", "/issues/edit/:id+"],
};
CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧
下一篇讲分配 Issue 给用户