发布于 2026-01-06 3 阅读
0

使用 NextJS 和 NextAuth v4 构建身份验证 DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

使用 NextJS 和 NextAuth v4 构建身份验证

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

今天我们将使用 Next.js 和 NextAuth 构建一个身份验证示例应用程序。我们将使用自定义表单和凭据提供程序,以便轻松地在项目中引入自定义数据库或端点进行凭据验证。如果您需要在项目中编写少量代码来实现身份验证功能,这是一个很好的样板。

什么是 NextAuth?
它是一个完整的 Next.js 应用开源解决方案。它旨在简化应用中多种用户身份验证方式的处理。它内置支持 OAuth、Google 等多种身份验证服务。NextAuth 也非常适合数据库身份验证,因为它提供了广泛的数据库支持。


演示

图像授权流程


 

项目设置

yarn create next-app app && cd app
Enter fullscreen mode Exit fullscreen mode
mkdir components && cd pages && touch login.js && cd api && mkdir auth 
Enter fullscreen mode Exit fullscreen mode
npm i next-auth axios
Enter fullscreen mode Exit fullscreen mode

 

我们先从编辑开始。_app.js
我们将导入SessionProvideruserSession。

  • 会话提供程序将允许我们向组件提供会话数据。

  • useSession 是一个客户端 React Hook,它允许我们确定用户是否已通过身份验证并获取用户数据。

我们将创建一个身份验证功能组件,该组件将允许我们确定是否应该允许用户访问某些页面,或者是否需要将用户重定向回登录页面。

import { SessionProvider, useSession } from 'next-auth/react'
import { useEffect } from 'react'
import { useRouter } from 'next/router'

export default function MyApp({ Component, pageProps: pageProps }) {
  return (
    <SessionProvider session={pageProps.session}>
      {Component.auth ? (
        <Auth>
          <Component {...pageProps} />
        </Auth>
      ) : (
        <Component {...pageProps} />
      )}
    </SessionProvider>
  )
}

function Auth({ children }) {
  const router = useRouter()
  const { data: session, status, token } = useSession()
  const isUser = !!session?.user
  useEffect(() => {
    if (status === 'loading') return // Do nothing while loading
    if (!isUser) router.push('/login') //Redirect to login
  }, [isUser, status])

  if (isUser) {
    return children
  }
  // Session is being fetched, or no user.
  // If no user, useEffect() will redirect.
  return <div>Loading...</div>
}
Enter fullscreen mode Exit fullscreen mode

 
现在我们将创建一个动态 API 路由来捕获所有路径,/api/auth因为 NextAuth 默认需要访问这些路由。凭据提供程序允许我们实现用户授权逻辑,这里我们需要数据库或 API 来验证用户凭据是否有效。如果出现错误,则会在登录表单中返回一条消息。为了简单起见,本示例中使用了硬编码的用户。我们将使用会话 cookie 中的加密 JWT(JWE)。

在 [...nextauth].js 目录下创建pages/api/auth文件

import NextAuth from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'

//Api route function that is returned from next auth
export default NextAuth({
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        // credentials will to passed from our login form
        // Your own logic here either check agains database or api endpoint
        // e.g. verify password if valid return user object.
        const user = {
          id: 1,
          name: 'john',
          email: 'user@example.com',
          password: '12345',
        }
        if (
          credentials.email === user.email &&
          credentials.password === user.password
        )
          return user
        throw new Error('Incorrect Credentials') // This will be error message displayed in login form
      },
    }),
  ],
  callbacks: {
    // called after sucessful signin
    jwt: async ({ token, user }) => {
      if (user) token.id = user.id
      return token
    }, // called whenever session is checked
    session: async ({ session, token }) => {
      if (token) session.id = token.id
      return session
    },
  },
  secret: 'SECRET_HERE',
  session: {
    strategy: 'jwt',
    maxAge: 1 * 24 * 60 * 60, // 1d
  },
  jwt: {
    secret: 'SECRET_HERE',
    encryption: true,
  },
})
Enter fullscreen mode Exit fullscreen mode

现在我们将实现login.js
登录函数,该函数会调用并传递用户详细信息进行授权。如果凭据匹配,用户将被授予访问权限并重定向到受保护的“/”路由。

import { signIn, useSession } from 'next-auth/react'
import { useRouter } from 'next/router'
import { useState, useRef } from 'react'

const Login = () => {
  const { status, loading } = useSession()
  const router = useRouter()
  const [error, setError] = useState(false)
  const emailRef = useRef()
  const passwordRef = useRef()

  if (status === 'authenticated') {
    router.push('/')
  }
  const loginHandler = async (e) => {
    e.preventDefault()
    const { error } = await signIn('credentials', {
      redirect: false,
      email: emailRef.current.value,
      password: passwordRef.current.value,
      callbackUrl: '/',
    })
    if (error) setError(error)
  }
  return (
    <>
      {status === 'unauthenticated' && (
        <>
          <p>{status}</p>
          <h3>{error}</h3>
          <h3>Log in</h3>
          <form onSubmit={(e) => loginHandler(e)}>
            <input placeholder='Email' name='email' ref={emailRef} />
            <input placeholder='Pasword' name='password' ref={passwordRef} />
            <input type='submit' />
          </form>
        </>
      )}
    </>
  )
}

export default Login

Enter fullscreen mode Exit fullscreen mode

index.js我们在 index.js 文件中Dashboard.auth = true将此路由标记为受保护路由。因此,只有经过身份验证的用户才能访问它。

import Navbar from '../components/Navbar'

export default function Dashboard() {
  return (
    <>
      <Navbar />
      <h1>secret dashboard</h1>
    </>
  )
}

Dashboard.auth = true
Enter fullscreen mode Exit fullscreen mode

最后,我们将创建一个带有注销按钮的导航栏,以便销毁会话并重定向到登录页面。

Navbar.js

import { signOut, useSession } from 'next-auth/react'

const Navbar = () => {
  const { data: session } = useSession()
  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'row',
        width: '100%',
        backgroundColor: '#b91c1c',
      }}
    >
      <a>{session.user.name}</a>
      &nbsp;
      <button
        onClick={() => {
          signOut({ redirect: false })
        }}
      >
        Signout
      </button>
    </div>
  )
}
export default Navbar
Enter fullscreen mode Exit fullscreen mode

 
总而言之,NextAuth 是一款功能强大且灵活的身份验证解决方案,并拥有便于开发者使用的文档。借助 NextAuth,我们可以简化应用程序的用户身份验证流程,并遵循良好的实践和模式。

希望这篇文章对你们有所帮助。感谢阅读!

Github 仓库

文章来源:https://dev.to/przpiw/build-authentication-with-react-and-nextauth-v4-2edc