在 Next.js 中创建用于身份验证的 HOC(高阶组件)
Create a HOC (higher order component) for authentication in Next.js
所以我在 Next.js 应用程序中创建身份验证逻辑。我在处理请求的地方创建了 /api/auth/login
页面,如果用户数据良好,我将创建一个带有 JWT 令牌的 httpOnly
cookie 并将一些数据返回到前端。那部分工作正常,但我需要一些方法来保护某些页面,以便只有登录的用户才能访问它们,我在为此创建 HOC 时遇到问题。
我看到的最好的方法是使用 getInitialProps
但在 Next.js 网站上它说我不应该再使用它了,所以我考虑使用 getServerSideProps
但那没有要么不起作用,要么我可能做错了什么。
这是我的 HOC 代码:
(cookie 存储在 userToken
名称下)
import React from 'react';
const jwt = require('jsonwebtoken');
const RequireAuthentication = (WrappedComponent) => {
return WrappedComponent;
};
export async function getServerSideProps({req,res}) {
const token = req.cookies.userToken || null;
// no token so i take user to login page
if (!token) {
res.statusCode = 302;
res.setHeader('Location', '/admin/login')
return {props: {}}
} else {
// we have token so i return nothing without changing location
return;
}
}
export default RequireAuthentication;
如果您对如何使用 cookie 处理 Next.js 中的身份验证有任何其他想法,我将不胜感激,因为我是服务器端呈现的新手 react/auth。
您应该将您的身份验证逻辑从 getServerSideProps
分离并提取到可重用的高阶函数中。
例如,您可以使用以下函数来接受另一个函数(您的 getServerSideProps
),如果 userToken
未设置,则会重定向到您的登录页面。
export function requireAuthentication(gssp) {
return async (context) => {
const { req, res } = context;
const token = req.cookies.userToken;
if (!token) {
// Redirect to login page
return {
redirect: {
destination: '/admin/login',
statusCode: 302
}
};
}
return await gssp(context); // Continue on to call `getServerSideProps` logic
}
}
然后您可以通过包装 getServerSideProps
函数在您的页面中使用它。
// pages/index.js (or some other page)
export const getServerSideProps = requireAuthentication(context => {
// Your normal `getServerSideProps` code here
})
根据 Julio 的回答,我让它适用于 iron-session
:
import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '@/utils/index'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))
然后我像这样使用它:
export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
return {
props: {},
}
})
我的 withSessionSsr
函数如下所示:
import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'
const IRON_OPTIONS: IronSessionOptions = {
cookieName: process.env.IRON_COOKIE_NAME,
password: process.env.IRON_PASSWORD,
ttl: 60 * 2,
}
function withSessionRoute(handler: NextApiHandler) {
return withIronSessionApiRoute(handler, IRON_OPTIONS)
}
// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
return withIronSessionSsr(handler, IRON_OPTIONS)
}
export { withSessionRoute, withSessionSsr }
所以我在 Next.js 应用程序中创建身份验证逻辑。我在处理请求的地方创建了 /api/auth/login
页面,如果用户数据良好,我将创建一个带有 JWT 令牌的 httpOnly
cookie 并将一些数据返回到前端。那部分工作正常,但我需要一些方法来保护某些页面,以便只有登录的用户才能访问它们,我在为此创建 HOC 时遇到问题。
我看到的最好的方法是使用 getInitialProps
但在 Next.js 网站上它说我不应该再使用它了,所以我考虑使用 getServerSideProps
但那没有要么不起作用,要么我可能做错了什么。
这是我的 HOC 代码:
(cookie 存储在 userToken
名称下)
import React from 'react';
const jwt = require('jsonwebtoken');
const RequireAuthentication = (WrappedComponent) => {
return WrappedComponent;
};
export async function getServerSideProps({req,res}) {
const token = req.cookies.userToken || null;
// no token so i take user to login page
if (!token) {
res.statusCode = 302;
res.setHeader('Location', '/admin/login')
return {props: {}}
} else {
// we have token so i return nothing without changing location
return;
}
}
export default RequireAuthentication;
如果您对如何使用 cookie 处理 Next.js 中的身份验证有任何其他想法,我将不胜感激,因为我是服务器端呈现的新手 react/auth。
您应该将您的身份验证逻辑从 getServerSideProps
分离并提取到可重用的高阶函数中。
例如,您可以使用以下函数来接受另一个函数(您的 getServerSideProps
),如果 userToken
未设置,则会重定向到您的登录页面。
export function requireAuthentication(gssp) {
return async (context) => {
const { req, res } = context;
const token = req.cookies.userToken;
if (!token) {
// Redirect to login page
return {
redirect: {
destination: '/admin/login',
statusCode: 302
}
};
}
return await gssp(context); // Continue on to call `getServerSideProps` logic
}
}
然后您可以通过包装 getServerSideProps
函数在您的页面中使用它。
// pages/index.js (or some other page)
export const getServerSideProps = requireAuthentication(context => {
// Your normal `getServerSideProps` code here
})
根据 Julio 的回答,我让它适用于 iron-session
:
import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '@/utils/index'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))
然后我像这样使用它:
export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
return {
props: {},
}
})
我的 withSessionSsr
函数如下所示:
import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'
const IRON_OPTIONS: IronSessionOptions = {
cookieName: process.env.IRON_COOKIE_NAME,
password: process.env.IRON_PASSWORD,
ttl: 60 * 2,
}
function withSessionRoute(handler: NextApiHandler) {
return withIronSessionApiRoute(handler, IRON_OPTIONS)
}
// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
return withIronSessionSsr(handler, IRON_OPTIONS)
}
export { withSessionRoute, withSessionSsr }