目录
初始设置
安装配置nextauthconfig 设置路由处理程序设置中间件在服务器端组件中获取会话在客户端组件中获取会话文件夹结构
实施身份验证:凭据和 google oauth
设置 prisma凭证添加 google oauth 提供商设置 google oauth 应用程序设置重定向 uri设置环境变量设置提供商创建登录和注册页面文件夹结构
初始设置
安装
npm install next-auth@beta
// env.localauth_secret=generatetd_random_value
配置
nextauthconfig 设置
// src/auth.tsimport nextauth from "next-auth"export const config = { providers: [],}export const { handlers, signin, signout, auth } = nextauth(config)
它应该放在src文件夹内
providers 在 auth.js 中表示是可用于登录用户的服务。用户可以通过四种方式登录。
使用内置的 oauth 提供程序(例如 github、google 等…)使用自定义 oauth 提供程序使用电子邮件使用凭证
https://authjs.dev/reference/nextjs#providers
路由处理程序设置
// src/app/api/auth/[...nextauth]/route.tsimport { handlers } from "@/auth" // referring to the auth.ts we just createdexport const { get, post } = handlers
此文件用于使用 next.js app router 设置路由处理程序。
中间件
// src/middleware.tsimport { auth } from "@/auth"export default auth((req) => { // add your logic here}export const config = { matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"], // it's default setting}
在src文件夹内写入。
如果写在 src 文件夹之外,中间件将无法工作。
中间件是一个允许您在请求完成之前运行代码的函数。它对于保护路由和处理整个应用程序的身份验证特别有用。
matcher 是 一个配置选项,用于指定哪些路由中间件应应用于。它有助于仅在必要的路由上运行中间件来优化性能。
示例匹配器: [‘/dashboard/:path*’] 仅将中间件应用于仪表板路由。
https://authjs.dev/getting-started/session-management/protecting?framework=express#nextjs-middleware
在服务器端组件中获取会话
// src/app/page.tsximport { auth } from "@/auth"import { redirect } from "next/navigation"export default async function page() { const session = await auth() if (!session) { redirect('/login') } return ( hello world!
@@##@@ )}
在客户端组件中获取会话
// src/app/page.tsx"use client"import { usesession } from "next-auth/react"import { userouter } from "next/navigation"export default async function page() { const { data: session } = usesession() const router = userouter() if (!session.user) { router.push('/login') } return ( hello world!
@@##@@ )}// src/app/layout.tsximport type { metadata } from "next";import "./globals.css";import { sessionprovider } from "next-auth/react"export const metadata: metadata = { title: "create next app", description: "generated by create next app",};export default function rootlayout({ children,}: readonly) { return ( {children} );}
文件夹结构
/src /app /api /auth [...nextauth] /route.ts // route handler layout.tsx page.tsx auth.ts // provider, callback, logic etc middleware.ts // a function before request
实施身份验证:凭据和 google oauth
设置棱镜
// prisma/schema.prismamodel user { id string @id @default(cuid()) name string? email string? @unique emailverified datetime? image string? password string? accounts account[] sessions session[]}model account { // ... (standard auth.js account model)}model session { // ... (standard auth.js session model)}// ... (other necessary models)
// src/lib/prisma.tsimport { prismaclient } from "@prisma/client"const globalforprisma = globalthis as unknown as { prisma: prismaclient }export const prisma = globalforprisma.prisma || new prismaclient()if (process.env.node_env !== "production") globalforprisma.prisma = prisma
证书
凭证,在身份验证的上下文中,指的是使用用户提供的信息验证用户身份的方法,通常是用户名(或电子邮件)和密码。
我们可以在 src/auth.ts 中添加凭据。
// src/auth.tsimport nextauth from "next-auth";import type { nextauthconfig } from "next-auth";import credentials from "next-auth/providers/credentials"import { prismaadapter } from "@auth/prisma-adapter"import { prisma } from "@/lib/prisma"import bcrypt from 'bcryptjs';export const config = { adapter: prismaadapter(prisma), providers: [ credentials({ credentials: { email: { label: "email", type: "text" }, password: { label: "password", type: "password" } }, authorize: async (credentials): promise => { if (!credentials?.email || !credentials?.password) { return null; } try { const user = await prisma.user.findunique({ where: { email: credentials.email as string } }) if (!user || !user.hashedpassword) { return null } const ispasswordvalid = await bcrypt.compare( credentials.password as string, user.hashedpassword ) if (!ispasswordvalid) { return null } return { id: user.id as string, email: user.email as string, name: user.name as string, } } catch (error) { console.error('error during authentication:', error) return null } } }) ], secret: process.env.auth_secret, pages: { signin: '/login', }, session: { strategy: "jwt", }, callbacks: { async jwt({ token, user }) { if (user) { token.id = user.id token.email = user.email token.name = user.name } return token }, async session({ session, token }) { if (session.user) { session.user.id = token.id as string session.user.email = token.email as string session.user.name = token.name as string } return session }, },} satisfies nextauthconfig;export const { handlers, auth, signin, signout } = nextauth(config);
适配器:
将身份验证系统连接到数据库或数据存储解决方案的模块。
秘密:
这是一个随机字符串,用于哈希令牌、签名/加密 cookie 以及生成加密密钥。这对于安全至关重要,应该保密。在本例中,它是使用环境变量 auth_secret 设置的。
页面:
此对象允许您自定义身份验证页面的 url。在您的示例中,signin: ‘/login’ 表示登录页面将位于 ‘/login’ 路由,而不是默认的 ‘/api/auth/signin’。
会话:
这配置了会话的处理方式。策略:“jwt”表示 json web token 将用于会话管理而不是数据库会话。
回调:
这些是在身份验证流程中的各个点调用的函数,允许您自定义流程。
jwt 回调:
它在创建或更新 jwt 时运行。在您的代码中,它将用户信息(id、电子邮件、姓名)添加到令牌中。
会话回调:
每当检查会话时都会运行。您的代码正在将用户信息从令牌添加到会话对象。
添加 google oauth 提供商
设置 google oauth 应用程序
从 gcp console 创建新的 oauth 客户端 id > api 和服务 > 凭据

创建后,保存您的客户端 id 和客户端密钥以供以后使用。
设置重定向 uri
当我们在本地工作时,设置http://localhost:3000/api/auth/callback/google
生产环境中,只需将 http://localhost:3000 替换为 https://—–即可。

设置环境变量
// .env.localgoogle_client_id={client_id}google_client_secret={client_secret}
设置提供商
// src/auth.tsimport googleprovider from "next-auth/providers/google" // add this import.export const { handlers, auth } = nextauth({ adapter: prismaadapter(prisma), providers: [ credentialsprovider({ // ... (previous credentials configuration) }), googleprovider({ clientid: process.env.google_client_id, clientsecret: process.env.google_client_secret, }), ], // ... other configurations})
https://authjs.dev/getting-started/authentication/oauth
创建登录和注册页面
//// ui pages// src/app/login/loginpage.tsximport link from 'next/link'import { loginform } from '@/components/auth/loginform'import { separator } from '@/components/auth/separator'import { authlayout } from '@/components/auth/authlayout'import { googleauthbutton } from '@/components/auth/googleauthbutton'export default function loginpage() { return ( do not have an account?{' '} sign up
)}// src/app/signup/signuppage.tsximport link from 'next/link'import { signupform } from '@/components/auth/signupform'import { separator } from '@/components/auth/separator'import { authlayout } from '@/components/auth/authlayout'import { googleauthbutton } from '@/components/auth/googleauthbutton'export default function signuppage() { return ( already have an account?{' '} sign in
)}
//// components// src/components/auth/authlayout.tsximport react from 'react'interface authlayoutprops { children: react.reactnode title: string}export const authlayout: react.fc = ({ children, title }) => { return ( {title}
{children} )}// src/components/auth/googleauthbutton.tsximport { signin } from "@/auth"import { button } from "@/components/ui/button"interface googleauthbuttonprops { text: string}export const googleauthbutton: react.fc = ({ text }) => { return ( { "use server" await signin("google", { redirectto: '/' }) }} > )}// src/components/auth/loginform.tsx'use client'import { usetransition } from "react"import { useform } from "react-hook-form"import { form, formcontrol, formfield, formitem, formlabel, formmessage,} from "@/components/ui/form"import { input } from "@/components/ui/input"import { button } from "@/components/ui/button"import { loginresolver, loginschema } from "@/schema/login"import { usestate } from "react"import { userouter } from "next/navigation"import { formerror } from "@/components/auth/formerror"import { formsuccess } from "@/components/auth/formsuccess"import { login } from "@/app/actions/auth/login"import { loader2 } from "lucide-react"export const loginform = () => { const [error, seterror] = usestate('') const [success, setsuccess] = usestate('') const [ispending, starttransition] = usetransition() const router = userouter(); const form = useform({ defaultvalues: { email: '', password: ''}, resolver: loginresolver, }) const onsubmit = (formdata: loginschema) => { starttransition(() => { seterror('') setsuccess('') login(formdata) .then((data) => { if (data.success) { setsuccess(data.success) router.push('/setup') } else if (data.error) { seterror(data.error) } }) .catch((data) => { seterror(data.error) }) }) } return ( ( email address )} /> ( password )} />
//// actions// src/app/actions/auth/login.ts'use server'import { loginschema, loginschema } from '@/schema/login'import { signin } from '@/auth'export const login = async (formdata: loginschema) => { const email = formdata['email'] as string const password = formdata['password'] as string const validatedfields = loginschema.safeparse({ email: formdata.email as string, password: formdata.password as string, }) if (!validatedfields.success) { return { errors: validatedfields.error.flatten().fielderrors, message: 'login failed. please check your input.' } } try { const result = await signin('credentials', { redirect: false, callbackurl: '/setup', email, password }) if (result?.error) { return { error : 'invalid email or password'} } else { return { success : 'login successfully'} } } catch { return { error : 'login failed'} }}// src/app/actions/auth/signup.ts'use server'import bcrypt from 'bcryptjs'import { signupschema, signupschema } from "@/schema/signup"import { prisma } from '@/lib/prisma';export const signup = async (formdata: signupschema) => { const validatedfields = signupschema.safeparse({ name: formdata.name as string, email: formdata.email as string, password: formdata.password as string, }) if (!validatedfields.success) { return { errors: validatedfields.error.flatten().fielderrors, message: 'sign up failed. please check your input.' } } try { const hashedpassword = await bcrypt.hash(validatedfields.data.password, 10); const existinguser = await prisma.user.findunique({ where: { email: validatedfields.data.email } }) if (existinguser) { return { error: 'user already exists!' } } await prisma.user.create({ data: { name: validatedfields.data.name, email: validatedfields.data.email, hashedpassword: hashedpassword, }, }); return { success: 'user created successfully!' } } catch (error) { return { error : `sign up failed`} }}
//// validations// src/schema/login.tsimport * as z from 'zod';import { zodresolver } from '@hookform/resolvers/zod'; export const loginschema = z.object({ email: z.string().email('this is not valid email address'), password: z .string() .min(8, { message: 'password must contain at least 8 characters' }),});export type loginschema = z.infer;export const loginresolver = zodresolver(loginschema);// src/schema/signup.tsimport * as z from 'zod';import { zodresolver } from '@hookform/resolvers/zod'; export const signupschema = z.object({ name: z.string().min(1, { message: 'name is required' }), email: z.string().email('this is not valid email address'), password: z .string() .min(8, { message: 'password must contain at least 8 characters' }),});export type signupschema = z.infer;export const signupresolver = zodresolver(signupschema);
// src/middleware.tsimport { nextresponse } from 'next/server'import { auth } from "@/auth"export default auth((req) => { const { nexturl, auth: session } = req const isloggedin = !!session const isloginpage = nexturl.pathname === "/login" const issignuppage = nexturl.pathname === "/signup" const issetuppage = nexturl.pathname === "/setup" // if trying to access /setup while not logged in if (!isloggedin && issetuppage) { const loginurl = new url("/login", nexturl.origin) return nextresponse.redirect(loginurl) } // if trying to access /login or /signup while already logged in if (isloggedin && (isloginpage || issignuppage)) { const dashboardurl = new url("/", nexturl.origin) return nextresponse.redirect(dashboardurl) } // for all other cases, allow the request to pass through return nextresponse.next()})export const config = { matcher: ["/login","/signup", "/setup", "/"],};
文件夹结构
/src /app /actions /login.ts // Login Action /signup.ts // Signup Action /api /auth [...nextauth] /route.ts /login page.tsx // Login Page /signup page.tsx // Sign Up Page layout.tsx page.tsx /components /auth AuthLayout.tsx GoogleAuthButton.tsx LoginForm.tsx SignupForm.tsx FormSuccess.tsx FormError.tsx Separator.tsx /schema login.ts signup.ts auth.ts // in src folder middleware.ts // in src folder


以上就是在 Nextjs App Router 中使用 Authjs 进行用户身份验证的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1496940.html
微信扫一扫
支付宝扫一扫