Next.js 身份验证完整指南
封面图片由Kai Pilger提供
在本指南中,你将学习如何在 Next.js 应用中实现身份验证。我将介绍客户端身份验证、经过身份验证的服务器渲染页面、经过身份验证的 API 路由、受保护的路由以及重定向。
身份验证服务将使用AWS Amplify实现,但这里介绍的想法和策略适用于任何身份验证服务,如Auth0 / Okta或甚至自定义后端实现,只要它提供一种跨客户端和服务器管理会话的方法。
Next.js 概述
Next.js 将客户端渲染与预渲染 HTML 相结合,形成静态页面和服务器渲染页面。该框架还可以轻松使用 API 路由创建 API。
运行构建时,框架将确定页面是静态生成还是服务器渲染。默认情况下,所有页面都是静态生成的,除非页面使用getServerSideProps
函数将 props 传递到页面中。此外,所有 API 路由默认都进行服务器渲染。
Next.js 身份验证概念
在 Next.js 应用中,你通常希望充分利用所有这些功能,并让你的 API 跨框架(客户端和服务器)无缝运行。但问题在于,在客户端和服务器上安全地访问用户会话通常并不容易。
在本指南中,我将向您展示如何启用用户身份验证和授权来实现以下功能:
- 客户端身份验证
- 在客户端访问用户会话
- 受保护的客户端路由
- 客户端重定向
- 在服务器端路由中访问用户会话(
getServerSideProps
) - 受保护的服务器路由(
getServerSideProps
) - 服务器端重定向(
getServerSideProps
) - 通过 API 路由访问用户会话
- 社交登录 (OAuth)
- 使用 Next.js 无服务器组件部署应用程序
入门
首先创建一个新的 Next.js 应用:
npx create-next-app next-authentication
接下来,进入新目录并安装依赖项:
cd next-authentication
npm install aws-amplify @aws-amplify/ui-react emotion
接下来,初始化一个新的 Amplify 项目:
amplify init
> Choose defaults when prompted
如果您尚未安装和配置 Amplify CLI,请观看此视频以获取完整的演示。
接下来,添加身份验证服务:
amplify add auth
? Do you want to use the default authentication and security configuration? Default configuration
? How do you want users to be able to sign in? Username
? Do you want to configure advanced settings? No, I am done.
接下来部署身份验证服务:
amplify push --y
启用 Amplify SSR
接下来,要启用 Amplify SSR 支持,请打开pages/_app.js并在文件顶部添加以下内容:
import Amplify from 'aws-amplify'
import config from '../src/aws-exports'
Amplify.configure({
...config,
ssr: true
})
🔥 只需设置ssr
为true
即可让您的 Amplify 应用具备 SSR 感知能力。
创建身份验证/配置文件路由
接下来,在pages目录中创建一个名为profile.js的新文件。
在这里,我们将使用withAuthenticator
组件启用身份验证。该组件将创建用户身份验证流程,使用户能够使用 MFA 注册并登录。
在此文件中,添加以下代码:
// pages/profile.js
import { useState, useEffect } from 'react'
import { Auth } from 'aws-amplify'
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'
function Profile() {
const [user, setUser] = useState(null)
useEffect(() => {
// Access the user session on the client
Auth.currentAuthenticatedUser()
.then(user => {
console.log("User: ", user)
setUser(user)
})
.catch(err => setUser(null))
}, [])
return (
<div>
{ user && <h1>Welcome, {user.username}</h1> }
<AmplifySignOut />
</div>
)
}
export default withAuthenticator(Profile)
最后,更新pages/_app.js以添加一些导航来链接页面:
import '../styles/globals.css'
import Link from 'next/link'
import { css } from 'emotion'
import Amplify from 'aws-amplify'
import config from '../src/aws-exports'
Amplify.configure({
...config,
ssr: true
})
export default function MyApp({ Component, pageProps }) {
return (
<div>
<nav className={navStyle}>
<Link href="/">
<span className={linkStyle}>Home</span>
</Link>
<Link href="/profile">
<span className={linkStyle}>Profile</span>
</Link>
</nav>
<Component {...pageProps} />
</div>
)
}
const linkStyle = css`
margin-right: 20px;
cursor: pointer;
`
const navStyle = css`
display: flex;
`
可选 - 设置组件样式
您可以为身份验证组件配置样式。例如,为了尝试匹配 Next.js 启动器附带的蓝色配色方案,您可以在style/globals.css的底部添加以下内容:
:root {
--amplify-primary-color: #0083e8;
--amplify-primary-tint: #006ec2;
--amplify-primary-shade: #006ec2;
}
创建帐户并登录
现在已经创建了个人资料路线,让我们通过创建新帐户并登录来测试它。
npm run dev
单击此处了解更多自定义
withAuthenticator
组件的方法。
您应该能够导航到/profile路线来创建帐户并登录。
直接使用 Auth 类
如果您想构建自己的自定义身份验证流程,您还可以利用Auth 类,它有超过 30 种用于管理用户身份验证状态的方法,包括signUp
、、和等confirmSignUp
方法。signIn
forgotPassword
通过 SSR 路由访问用户会话
现在用户可以登录,让我们创建一个新的路线来测试 SSR。
在 pages 目录中创建一个名为/protected.js的新路由。
在这里,我们希望有一条路由在服务器上对用户进行身份验证,并根据用户的身份验证状态返回成功或错误消息。
// pages/protected.js
import { withSSRContext } from 'aws-amplify'
function Protected({ authenticated, username }) {
if (!authenticated) {
return <h1>Not authenticated</h1>
}
return <h1>Hello {username} from SSR route!</h1>
}
export async function getServerSideProps(context) {
const { Auth } = withSSRContext(context)
try {
const user = await Auth.currentAuthenticatedUser()
console.log('user: ', user)
return {
props: {
authenticated: true, username: user.username
}
}
} catch (err) {
return {
props: {
authenticated: false
}
}
}
}
export default Protected
然后使用指向新路线的链接更新pages/_app.js中的导航:
<Link href="/protected">
<span className={linkStyle}>Protected route</span>
</Link>
现在,当您登录后,您将能够在该getServerSideProps
方法中访问已验证的用户。您还应该看到用户对象已从终端注销。
这是通过使用withSSRContext
函数解构Auth
并aws-amplify
调用 来实现的Auth.currentAuthenticatedUser()
。当以这种方式访问该类Auth
时,Amplify 将自动读取请求对象,并允许您在 API 路由和 SSR 路由上访问已登录用户的会话。
通过 API 路由访问用户会话
在此 API 路由中,我们希望访问用户,对于未经身份验证的用户返回 null,对于经过身份验证的用户返回用户名。
为此,在pages/api中创建一个名为check-user.js 的新文件:
// pages/api/check-user.js
import Amplify, { withSSRContext } from 'aws-amplify'
import config from "../../src/aws-exports.js"
// Amplify SSR configuration needs to be enabled within each API route
Amplify.configure({ ...config, ssr: true })
export default async (req, res) => {
const { Auth } = withSSRContext({ req })
try {
const user = await Auth.currentAuthenticatedUser()
res.json({ user: user.username })
} catch (err) {
res.statusCode = 200
res.json({ user: null })
}
}
当您导航或尝试访问/api/check-user时,您会注意到,当您通过身份验证时用户对象可用,而当您未通过身份验证时则不可用。
客户端重定向
通常,您需要检测用户是否已登录,并根据用户是否经过身份验证或根据其凭据允许访问或重定向他们。
为此,您可以使用withRouter
Next.js 中的钩子,根据用户状态以编程方式进行路由。让我们尝试一下。
在pages目录中创建一个名为protected-client-route.js的新文件。
在这里添加如下代码:
import { useState, useEffect } from 'react'
import { Auth } from 'aws-amplify'
import { useRouter } from 'next/router'
function ProtectedClient() {
const [user, setUser] = useState(null)
const router = useRouter()
useEffect(() => {
Auth.currentAuthenticatedUser()
.then(user => setUser(user))
// if there is no authenticated user, redirect to profile page
.catch(() => router.push('/profile'))
}, [])
if (!user) return null
return <h1>Hello {user.username} from client route!</h1>
}
export default ProtectedClient
接下来,在pages/_app.js中添加此路由的链接:
<Link href="/protected-client-route">
<span className={linkStyle}>Protected client route</span>
</Link>
如果您尝试访问受保护的客户端路由,如果您未通过身份验证,您将被自动重定向到配置文件路由,如果您通过身份验证,则允许您查看页面。
服务器端重定向
SSR 的优点之一是能够实现服务器端重定向。使用服务器端重定向更安全,因为您可以选择不渲染任何 HTML,而是将用户重定向到另一个页面。
打开pages/protected.js并使用以下代码进行更新:
// pages/protected.js
import { withSSRContext } from 'aws-amplify'
function Protected({ username }) {
return <h1>Hello {username} from SSR route!</h1>
}
export async function getServerSideProps({ req, res }) {
const { Auth } = withSSRContext({ req })
try {
const user = await Auth.currentAuthenticatedUser()
return {
props: {
authenticated: true,
username: user.username
}
}
} catch (err) {
res.writeHead(302, { Location: '/profile' })
res.end()
}
return {props: {}}
}
export default Protected
当您尝试访问此路线时,如果您未登录,您将被重定向到个人资料路线。
社交登录 (OAuth)
要添加社交登录,请运行amplify update auth
并选择使用社交提供商应用默认配置。
您可以从这里添加 Google、Facebook 或 Amazon 的社交登录。
一旦启用社交登录,您就可以使用以下代码从您的应用程序登录用户:
// username / password + all OAuth providers
Auth.federatedSignIn()
// specifying an OAuth provider
<button onClick={() => Auth.federatedSignIn({provider: 'Facebook'})}>Open Facebook</button>
<button onClick={() => Auth.federatedSignIn({provider: 'Google'})}>Open Google</button>
<button onClick={() => Auth.federatedSignIn({provider: 'Amazon'})}>Open Amazon</button>
使用无服务器框架将 Next.js 应用部署到 AWS
要使用无服务器框架和无服务器下一个组件serverless.yml
将应用程序部署到 AWS,首先在应用程序的根目录下创建一个名为的文件。
接下来,添加以下两行配置(您可以将myNextApp更改为您想要使用的任何名称):
myNextApp:
component: "@sls-next/serverless-component@1.17.0"
接下来,使用以下方式部署npx
:
npx serverless
如果您从未使用过 AWS CLI,则可能需要配置您的 AWS 凭证。请参阅此处的基本说明
视频演示
结论
该项目的最终代码位于此处
向 Amplify 团队的Eric Clemmons致以最诚挚的谢意,他领导了这个项目并将此功能融入 Amplify。
对于第 2 部分,我们将学习如何结合 Auth 和 Data 来完成以下任务:
getStaticPaths
在 SSG 期间提取数据进行补充- 在 API 路由中进行经过身份验证的 API 调用
- 在
getServerSideProps