当我重新加载页面时,用户就会注销

问题描述 投票:0回答:1

我正在构建一个 nextjs 13 管理仪表板,我只有一个问题,每当用户重新加载页面时,它就会重定向到登录页面,当检查开发人员模式时,cookie 仍然存在于 Cookie 部分,但会话存储和区域设置存储为空,并且每次登录令牌发生变化时 到目前为止我还没有实现中间件。

api/auth/[...nextauth]/route.js

import {authOptions} from '@/lib/auth'
import NextAuth from 'next-auth/next'

const handler = NextAuth(authOptions)

export {handler as GET, handler as POST}

/lib/auth.js

import { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { db } from "./db";
import { compare } from "bcryptjs";

export const authOptions = {
  adapter: PrismaAdapter(db),
  secret: process.env.NEXTAUTH_SECRET,
  session: {
    strategy: "jwt",
    maxAge: 60 * 60 * 24 * 30,
  },
  pages: {
    signIn: "/sign-in",
  },
  rememberMe: {
    label: "Remember Me",
    type: "checkbox",
  },
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: {
          label: "Email",
          type: "email",
          placeholder: "[email protected]",
          validation: {
            required: true,
            pattern:
              /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
          },
        },
        password: { label: "Password", type: "password", placeholder: "****" },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          return null;
        }

        const existingUser = await db.user.findUnique({
          where: { email: credentials?.email },
        });

        if (
          (existingUser &&
            existingUser.passwordExpirationDate &&
            existingUser.passwordExpirationDate < new Date()) ||
          (existingUser &&
            existingUser.verificationCodeExpiresAt &&
            existingUser.verificationCodeExpiresAt < new Date())
        ) {
          return null; // Trigger password reset
        }

        if (!existingUser) {
          return null;
        }
        const passwordMatch = await compare(
          credentials.password,
          existingUser.password
        );
        if (!passwordMatch) {
          return null;
        }

        const passwordAgeInDays = existingUser.lastPasswordResetDate
          ? Math.floor(
              (new Date().getTime() -
                existingUser.lastPasswordResetDate.getTime()) /
                (1000 * 60 * 60 * 24)
            )
          : 0;

        if (passwordAgeInDays > 30) {
          await db.user.update({
            where: { id: existingUser.id },
            data: { IsExpired: true },
          });
          return null;
        }
        const maxAge = credentials.rememberMe ? 60 * 60 * 24 * 30 : 60 * 60 * 24;
        return {
          id: `${existingUser.id}`,
          name: existingUser.name,
          username: existingUser.username,
          email: existingUser.email,
          role: existingUser.role,
          exp: Math.floor(Date.now() / 1000) + maxAge,
        };
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user }) {

      if (user) {
        return {
          ...token,
          id: user.id,
          username: user.username,
          role: user.role,
        };
      }
      return token;
    },
    async session({ session, token }) {
      const maxAge = token.exp - Math.floor(Date.now() / 1000);
      return {
        ...session,
        user: {
          ...session.user,
          id: token.id,
          username: token.username,
          role: token.role,
        },
        expires: Math.floor(Date.now() / 1000) + maxAge,
      };
    },
  },
};

我想保持用户登录状态,并管理用户会话

javascript authentication next.js session-cookies next.js13
1个回答
0
投票

理解问题:

页面重新加载后,尽管存在 cookie,用户仍会被重定向到登录页面。 重新加载后会话存储和本地存储为空。 每次登录时登录令牌都会发生变化(这对于持久会话来说是不可取的)。 根本原因(潜在):

Cookie 中安全标志缺失或不正确:使用 HTTPS 时,安全标志对于纯 HTTP Cookie 至关重要,以确保它们仅通过安全连接传输。这对于会话安全至关重要。 客户端存储滥用:虽然 cookie 旨在用于会话管理,但会话存储和本地存储不适合持久化用户会话,因为它们缺乏服务器端持久性并且可以由用户清除。 令牌刷新机制未实现:在令牌过期之前刷新令牌有助于维持持久会话,而无需持续进行身份验证。 解决方案:

设置 Cookie 的安全标志(需要 HTTPS):

在 NextAuth 配置 (/lib/auth.js) 中,确保 session.cookie 对象包含设置为 true 的 secure 属性:

JavaScript

authOptions = {
  // ... other options
  session: {
    strategy: "jwt",
    maxAge: 60 * 60 * 24 * 30,
    cookie: {
      secure: process.env.NODE_ENV === "production", // Set to true in production
    },
  },
  // ...
};

说明:这可确保 cookie 仅通过 HTTPS 连接传输,从而增强安全性。

服务器端保留会话数据:

利用 NextAuth 的内置 JWT 功能进行会话管理。 NextAuth 将会话数据(用户信息)安全地存储在签名的 JWT 令牌(JSON Web 令牌)中。当用户发出后续请求时,包含令牌的 cookie 会一起发送,从而允许 NextAuth 验证用户的会话。

将 NextAuth 的 session.strategy 配置为“jwt”,就像您已经完成的那样。 在 API 路由中实现自定义逻辑,以在令牌过期之前更新令牌,从而确保持续的身份验证。 (有关更多详细信息,请参阅 NextAuth 文档:https://next-auth.js.org/configuration/nextjs) 地址更改登录令牌:

JWT 令牌有一个过期时间(在会话配置中使用 maxAge 设置)。 NextAuth 在身份验证成功后自动生成新令牌。考虑在客户端实现令牌刷新机制,以便在当前令牌过期之前获取新令牌:

使用 NextAuth 的内置 useSession 挂钩来访问组件中的会话数据。 监控令牌的过期时间。 当令牌接近到期时,向 NextAuth 的 /api/auth/refresh 端点(通常由 NextAuth 提供)发起刷新请求以获取新令牌。

© www.soinside.com 2019 - 2024. All rights reserved.