文章第一句话为“这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
首先是安装插件,我们要用 next-auth 插件实现我们的功能
npm install next-auth
在 pages/api/auth 下建立 [...nextauth].ts 文件
providers 是配置登录方式及界面,可以接入 github、邮箱这些,GredentialsProvider 表示定义密码登录,由于本次是采用自定义登录界面方式,所以 credentials 中并没有内容
export default function auth(req: NextApiRequest, res: NextApiResponse) { return NextAuth(req, res, { secret: process.env.NEXTAUTH_SECRET, providers: [ CredentialsProvider({ name: "credentials", credentials: {}, async authorize(credentials, req) { try { if (req.body === undefined) { return null; } return req.body; } catch (e) { return null; } }, }), ], session: { strategy: "jwt", maxAge: 60 * 24 * 7, }, jwt: { maxAge: 60 * 24 * 7, }, pages: { signIn: "/auth/signIn", }, callbacks: { async jwt({ token, user, account, }: { token: JWT & { user: USER }; user: USER; account: Account | null | undefined; }) { // 在登陆时判断是否是自定义登录的方式,并将用户信息保存到next-auth生成的token中,(因为next-auth最终提供的用户信息很少,不能满足需要,因此需要我们自己通过传递设置) if (account && account.type === "credentials" && user) { token.user = user; token.accessToken = user.accessToken; } return token; }, async session({ session, token, }: { session: { user: USER; expires: Date; }; token: JWT & { user: USER }; }) { session.user = token.user; return session; }, }, });}
接下来在 _app.tsx 配置拦截
const Auth = ({ children }: { children: ReactElement }) => { const { data: session } = useSession({ required: true }); const user = session?.user; if (user) { return children; } return <div>...loading</div>;};export default function App({ Component, pageProps: { session, ...pageProps },}: AppPropsWithLayout) { const getLayout = Component.getLayout || ((page) => page); return ( <SessionProvider session={session}> {/* where the page has auth property, it should be sign in to visit */} {Component.auth ? ( <Auth> getLayout( <Component {...pageProps} />) </Auth> ) : ( getLayout(<Component {...pageProps} />) )} </SessionProvider> );}
这个 auth 属性来表示是否对页面进行拦截操作,需要手动设置
自定义登录界面,需要通过 signIn 函数来进行登录,为了避免浏览器跨域问题,先请求 Next.js API,Next.js API 会在后台访问真正的登录接口,同时为了正确处理登录信息,避免刷新,判断登录成功后再调用 signIn 函数
const retPromise = await fetch("/api/signIn", { method: "POST", body: JSON.stringify(data), }); const ret = await retPromise.json(); if (ret.id) { console.log(data); data["id"] = ret.id; signIn("credentials", {...data, callbackUrl:"http://localhost:3000/"}); } else { messageApi.error("登录失败,请确认账户密码是否正确"); }
退出登录
session 获取,getSession 用于在服务端和客户端都可以获取,同时不需要组件被 SessionProvider 包裹,在函数中也可以使用,useSession 用于在客户端获取,并且需要组件被 SessionProvider 包裹下使用