[ Next.js 登录鉴权 | 青训营笔记]

411 阅读36分钟

文章第一句话为“这是我参与「第五届青训营 」伴学笔记创作活动的第 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 包裹下使用