如何在Next.js中实现用户授权(附代码)

1,469 阅读9分钟

本教程的重点是帮助开发者学习如何将用户授权的责任委托给其他一些服务,如GitHub或Google,而不是在他们的应用程序中管理。

Next.js中的认证与授权

认证是验证用户是否是他们所声称的人的行为。用户名和密码是最常见的认证因素。

在验证用户时,如果用户输入了正确的数据,服务器就会认为该身份是有效的,并授予用户访问服务器资源的权限。

另一方面,安全系统中的授权是给予用户访问服务器上特定资源或功能的许可的过程。这个术语经常与访问控制或客户权限互换使用。

通常情况下,认证先于授权;用户应首先证明他们的身份是真实的,然后由后台管理员授予他们访问所请求的资源的权限。

介绍一下我们在Next.js中的用户授权项目

OAuth2.0是一个行业标准的授权协议,它使互联网用户能够与第三方网站和应用程序分享他们的账户信息,而不必给出他们的账户凭证。

本指南使用NextAuth.js库,在Next.js应用程序中实现用户授权(OAuth2.0)。

NextAuth.js是一个成熟的认证和授权解决方案,用于Next.js,旨在与任何OAuth服务合作。它内置了对许多流行的登录服务的支持,包括谷歌和GitHub。

创建一个Next.js应用程序

让我们从创建一个新的Next.js项目开始。

下面的命令显示了如何使用npm、Yarn和pnpm分别创建一个新的Next.js项目。打开终端,运行其中一个:

npx create-next-app@latest
# or
yarn create next-app
# or
pnpm create next-app

CLI会提示你为你的新项目提供一个名称。在命令提示符中输入你喜欢的名字,然后点击键盘上的enter ,完成安装。 安装完成后,运行以下命令,在你的本地机器上启动开发服务器:

npm run dev
# or 
yarn dev 
# or 
pnpm dev

默认情况下,这将在3000端口启动你的开发服务器。启动你的网络浏览器,导航到http://localhost:3000。你应该看到Next.js的启动页面,如下图所示:

Next Js Starter Page Shown When Opened In Browser At Localhost 3000 With Welcome Message And Links To Documentation, Course, Examples, And Vercel Deployment

接下来,你将在GitHub上创建一个新的OAuth应用,以便用OAuth注册你的应用程序。

创建一个GitHub OAuth应用程序

我们希望用户能用他们的GitHub账户登录,并授权我们的应用访问他们账户中的某些信息。

要做到这一点,我们需要首先创建一个新的GitHub OAuth应用。点击 "New OAuth app",并在表格中相应填写你的网站信息。这里有一些关于表格所要求的信息的重要事项需要注意:

  • 在 "应用程序名称 "栏中,输入你的应用程序的名称(例如,"我的应用程序")。
  • 在 "主页URL "字段中,键入您网站主页的完整URL
    • 由于我们处于开发模式,所以是这样的。 [http://localhost:3000](http://localhost:3000)
  • 在 "授权回调URL "字段中,输入你的应用程序的回调URL

授权回调 URL 是你希望 GitHub 在用户授权你的应用后重定向到的 URL。它应该是你的主页URL加上/api/auth/callback

在这种情况下,完整的URL应该是 [http://localhost:3000/api/auth/callback](http://localhost:3000/api/auth/callback).

其他字段其实并不重要,因此可以留空。 注册完GitHub应用后,你会被转到一个包含OAuth客户端ID的页面。点击 "Generate a new secret key",也可以生成一个客户秘密密钥。

为下一步保留这两个密钥。

将API密钥添加到环境变量中

Next.js内置了对环境变量的支持。要在你的项目中使用它们,在你的项目目录根部创建一个名为.env.local 的文件:

touch .env

打开.env.local ,添加以下内容:

GITHUB_ID=<your-client-id>
GITHUB_SECRET=<your-client-secret>
NEXTAUTH_URL=http://localhost:3000

用你在GitHub上的实际值替换上述模板。

安装NextAuth.js并配置GitHub提供商

现在你已经在GitHub上创建了一个OAuth应用程序,现在是时候设置前端应用程序了。

首先,通过运行以下命令安装NextAuth.js:

npm install next-auth --save

安装好库后,你现在必须在pages/api/auth ,创建一个名为[...nextauth].js 的文件。这个文件将包含你想在应用中使用的所有提供者。每个提供者都将使用凭证进行配置,以便应用程序能够成功地与我们的OAuth身份提供者连接。

由于我们在GitHub注册,我们将只使用GitHub提供商。在新创建的[...nextauth].js 文件中添加以下代码:

import NextAuth from 'next-auth'
import GitHubProvider from "next-auth/providers/github";

const options = {
    providers: [
        GitHubProvider({
            clientId: process.env.GITHUB_ID,
            clientSecret: process.env.GITHUB_SECRET
        }),
    ],
}

export default (req, res) => NextAuth(req, res, options)

首先,我们导入了NextAuthGitHubProvider 。然后,我们用我们的GITHUB_IDGITHUB_SECRET 环境变量配置了GitHubProvider ,这些变量是从process.env 中获取的。

最后一行导出一个函数,该函数返回NextAuth ,并将options 变量作为第三个参数。

这就是我们需要将我们的应用程序与GitHub连接起来的全部内容!

要看到这个效果,请用npm run dev 运行你的开发服务器。使用 next-auth 提供的 REST API,你可以用你的 GitHub 账户登录应用程序。导航到http://localhost:3000/api/auth/signin,你应该看到下面的内容:

Button Prompt To Sign Into Web App With Github Account

点击该按钮,你会被引导到GitHub的同意页面,告诉你要授权该应用。如果你这样做了,你就会在GitHub上签名。然而,应用程序不会反映你已经登录,因为我们还没有在应用程序中获得用户会话数据。接下来我们来解决这个问题

访问用户会话的方法是<SessionProvider>

当用户授权我们的应用程序时,你需要通过在我们的应用程序的前端呈现用户的详细信息来向用户显示他们已经登录。为了使这些工作顺利进行,我们必须首先用以下语言来包装我们的整个应用程序 [<SessionProvider>](https://next-auth.js.org/getting-started/client#sessionprovider).

在你的pages目录下创建一个_app.js 文件(如果它还不存在的话)并添加以下代码:

import { SessionProvider } from "next-auth/react"
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return (
    <SessionProvider session={pageProps.session}>
      <Component {...pageProps} />
    </SessionProvider>
  )
}

export default MyApp

有了这段代码,应用程序中的所有页面都可以访问用户会话,并且这个会话在网站导航期间将被保留。该会话也将与OAuth提供者共享。这样一来,应用程序就不会在用户每次访问网站时重新认证。

检查用户的登录状态useSession()

useSession() 钩子允许我们检查用户的登录状态并检索用户的会话信息。我们将使用这个钩子以及signInsignOut 来实现一个<Header> 组件,它可以检查用户是否通过了认证,并显示一个 "登录 "或 "退出 "链接。

打开components/Header.js 文件,从NextAuth.js客户端库中导入useSessionsignInsignOut

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

signIn 和 将被用于登录用户进出我们的应用程序。我们需要创建 和 方法来触发这两个功能。signOut handleSignin handleSignout

const handleSignin = (e) => {
      e.preventDefault()
      signIn()
  }    
const handleSignout = (e) => {
      e.preventDefault()
      signOut()
    }

接下来,让我们检索用户的会话数据:

const { data: session } = useSession();

一旦检索到数据,它就可以在页面上显示给用户,或者用JavaScript进行操作。让我们使用返回的细节来有条件地呈现一个签到和签出按钮。

用以下代码替换components/Header.js 中返回语句中的所有内容:

<div className='header'>
      <Link href='/'>
        <a className='logo'>NextAuth.js</a>
      </Link>
           {session && <a href="#" onClick={handleSignout} className="btn-signin">Sign out</a>  } 
           {!session && <a href="#" onClick={handleSignin}  className="btn-signin">Sign in</a>  } 
    </div>

你的Header.js 文件现在应该看起来像这样:

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

export default function Header() {  
    const handleSignin = (e) => {
        e.preventDefault()
        signIn()
    }

    const handleSignout = (e) => {
        e.preventDefault()
        signOut()
    }

    const { data: session } = useSession();

    return (
      <div className='header'>
        <Link href='/'>
          <a className='logo'>AppLogo</a>
        </Link>
             {session && <a href="#" onClick={handleSignout} className="btn-signin">SIGN OUT</a>  } 
             {!session && <a href="#" onClick={handleSignin}  className="btn-signin">SIGN IN</a>  } 
      </div>

    )
  }

接下来,我们将检索用户信息,并在授权我们的应用程序时将其显示给用户。

检索和显示用户信息

在我们的pages/index.js 文件中,我们需要根据用户的认证状态来显示和有条件地呈现用户的详细信息。

如果用户已经登录,我们将使用会话状态的数据渲染他们的个人资料图片、姓名和照片。用户将被授权查看或与所有的应用程序页面互动,以及注销。

如果用户没有登录,我们将呈现一个假的用户资料图片和文本,指示他们登录并授权应用程序访问他们的GitHub资料信息。用户将不会被授权查看或与应用程序的任何其他部分互动。

要做到这一点,请将你的index.js 文件内容替换为以下内容:

import Head from 'next/head
import Header from '../components/Header'
import styles from '../styles/Home.module.css'
import { useSession } from 'next-auth/react'

export default function Home() {
  const { data: session, status } = useSession()
  const loading = status === "loading"

  return (
    <div className={styles.container}>
      <Head>
        <title>Nextjs | Next-Auth</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Header />
      <main className={styles.main}>        
        <div className={styles.user}>
           {loading && <div className={styles.title}>Loading...</div>}
           {
            session &&
              <>
                <h1 className={styles.title}>Welcome, {session.user.name ?? session.user.email}!</h1>
               <p style={{ marginBottom: '10px' }}> </p> <br />
               <img src={session.user.image} alt="" className={styles.avatar} />
              </>
            }
           {
            !session &&
              <>
               <p className={styles.title}>Please log in to continue</p>
               <img src="no-user.jpg" alt="" className={styles.avatar} />               
              </>
           }
         </div>
      </main>
    </div>
  )
}

这是我注销时的页面:

Web App Page Showing Dummy User Profile Image And Login Instruction Text When User Is Not Logged In

这是我用GitHub账户登录后的页面:

Web App Page Showing User Details When User Is Signed In With Github

请随时在GitHub上抓取完整的源代码!

获取额外的范围数据

请注意,GitHub OAuth Provider 的默认作用域是read:users ,它授权你的应用程序读取用户的个人资料数据,如姓名、电子邮件和个人资料图片。

为了从默认范围以外的范围获取数据,你需要在pages/api/auth/[...nextauth].js 目录内提供商的配置对象中定义这些范围。

例如,让我们通过将authorization.params.scope 属性添加到GitHubProvider ,来请求读取用户的通知以及他们的个人资料数据:

// imports

const options = {
    providers: [
        GitHubProvider({
            clientId: process.env.GITHUB_ID,
            clientSecret: process.env.GITHUB_SECRET,
            // add this:                     
            authorization: { params: { scope: 'notifications' } },
        }),
    ],
}

// export 

在浏览器上导航到http://localhost:3000,并尝试用同一个 GitHub 账户登录。你应该得到以下结果:

User Prompt To Grant Additional Permissions Authorizing App To Read User Notifications With Information About What Access Is Granted And Buttons To Cancel Or Grant Authorization

结论

使用NextAuth.js库,你现在应该能够配置一个Next.js应用程序,以使用OAuth流程进行用户授权。NextAuth.js库为许多流行的登录服务提供了内置支持,使API集成的过程变得快速而简单。