NextJs 在客户端和服务器之间共享 JWT

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

我的 NextJs 应用程序中有 HTTP 服务,用于管理对外部后端服务器的 API 调用,就像 API 端点一样(NodeJs 应用程序与 NextJs 没有任何联系)

问题是: 我必须登录用户并取回 JWT。 我必须将 JWT 保存在某处,以便我的 NextJs HTTP 服务在每个请求之前将令牌注入标头,并且它可以从客户端和服务器端组件运行,因为现在,从其中一个组件发送它是未定义的,我相信这是因为它已保存饼干中只包含一个而不是两者

这是我的 HTTP 服务:

       const headers: Record<string, string> = {
  language: "en",
  Accept: "application/json",
  "Content-Type": "application/json",
};

type CustomRequestInit = RequestInit & {};

abstract class HttpFactory {
  abstract createHttp(url: string): Http;
}

abstract class Http {
  abstract builtUrl: string;

  abstract updateToken(_?: string): void;

  abstract get<T>(url: string, config?: CustomRequestInit): Promise<T>;
  abstract delete<T>(url: string, config?: CustomRequestInit): Promise<T>;
  abstract put<T>(
    url: string,
    data?: any,
    config?: CustomRequestInit,
  ): Promise<T>;
  abstract post<T>(
    url: string,
    data?: any,
    config?: CustomRequestInit,
  ): Promise<T>;
  abstract patch<T>(
    url: string,
    data?: any,
    config?: CustomRequestInit,
  ): Promise<T>;

  protected buildPath(path: string, trailingPath: string) {
    return `${path}${trailingPath}`;
  }
}

// FetchHttpFactory class implementing HttpFactory
class FetchHttpFactory extends HttpFactory {
  createHttp(url?: string): Http {
    return new FetchHttp(url);
  }
}

// FetchHttp class extending Http and implementing fetch methods
class FetchHttp extends Http {
  builtUrl: string;
  headers = headers || {};
  baseUrl = "http://localhost";
  token?: string = undefined;

  constructor(mainUrl?: string) {
    super();
    this.builtUrl = `${this.baseUrl}/api${mainUrl}`;
  }

  updateToken(token: string) {
    this.token = token;

    this.headers = {
      authorization: token,
    };
  }

  private async handleResponse<T>(response: Response): Promise<T> {
    const data = await response.json();
    return data;
  }

  async get<T>(url: string, config?: CustomRequestInit): Promise<T> {
    const { ...requestConfig } = config ?? ({} as CustomRequestInit);
    const response = await fetch(`${this.builtUrl}/${url}`, {
      method: "GET",
      headers: { ...this.headers, ...(requestConfig.headers || {}) },
      ...requestConfig,
    });

    return this.handleResponse<T>(response);
  }

  async post<T>(
    url: string,
    data?: any,
    config?: CustomRequestInit,
  ): Promise<T> {
    const { ...requestConfig } = config ?? ({} as CustomRequestInit);
    const response = await fetch(`${this.builtUrl}${url}`, {
      method: "POST",
      headers: { ...this.headers, ...(requestConfig.headers || {}) },
      body: JSON.stringify(data),
      ...requestConfig,
    });

    return this.handleResponse<T>(response);
  }
}

export { HttpFactory, FetchHttpFactory };

这是我的登录页面:

"use client";
import { useState } from "react";
import { useDispatch } from "react-redux";
import { useRouter } from "next/navigation";
import { Button, Label, TextInput } from "flowbite-react";

import API from "@/api";
import { AuthActions } from "@/store/reducers/auth";
import { navPageWrapper } from "@/hoc/navPageWrapper";

const SignIn = () => {
  const router = useRouter();
  const dispatch = useDispatch();

  const [isLoading, setIsLoading] = useState(false);

  const handleSubmit = async (e: any) => {
    e.preventDefault();

    const form = new FormData(e.target);
    const email = form.get("email")?.toString();
    const password = form.get("password")?.toString();

    if (!email || !password) return;

    setIsLoading(true);

    try {
      const response = await API.DB.Auth.signIn({ email, password });

      if ("token" in response) {
        const token = response.token;

        // where to save the token so that I can retrieve it in the HTTP service

        dispatch(AuthActions.setToken(token));
        router.push("/home");
      }
    } catch (err: any) {
      console.error("SignIn error:", err.message);
    }

    setIsLoading(false);
  };

  return (
    <div className="flex flex-1 items-center justify-center">
      <form
        onSubmit={handleSubmit}
        className="mx-2 w-11/12 rounded p-10 shadow-md sm:w-1/2 md:w-2/5 lg:w-3/12"
      >
        <div>
          <Label value="Email" />
          <TextInput
            value={"[email protected]"}
            name="email"
            placeholder="Email"
            type="text"
            required
          />
        </div>

        <div className="mt-4">
          <Label value="Password" />
          <TextInput
            value={"12345678@a"}
            name="password"
            placeholder="Password"
            required
          />
        </div>

        <div className="mt-4 flex flex-1 justify-end">
          <Button
            color="dark"
            type="submit"
            className="w-1/2 shadow"
            isProcessing={isLoading}
          >
            Submit!
          </Button>
        </div>
      </form>
    </div>
  );
};

export default navPageWrapper(SignIn);
reactjs next.js jwt
1个回答
0
投票

根据评论,我可以向您展示我的解决方案。

您可以使用适用于 OAuth2 或 JWT 的 Next-Auth 库。 您需要像这样创建凭据提供程序:

import Credentials from "next-auth/providers/credentials";
export const authOptions: NextAuthOptions = {
providers: [
    Credentials({
        name: 'Credentials',
        credentials: {
            username: { label: "Username", type: "text" },
            password: { label: "Password", type: "password" }
        },
        async authorize(credentials) {
            const res = await fetch(<Backend authorization endpoint, {
                method: 'POST',
                body: JSON.stringify({ username: credentials?.username, password: credentials?.password }),
                headers: { "Content-Type": "application/json" }
            })

            const user = await res.json()

            if (res.ok && user) {
                return user
            }

            return null
        }
    })
],

callbacks: {
    <callbacks>
},

session: {
    strategy: 'jwt',
    maxAge: <session_valid_time>,
},

pages: {
    signIn: '/login',
    error: '/<some_err_page>'
}

}

在布局中,您需要使用 import NextAuthSessionProvider from "./SessionProvider" 来包装所有 {children};

然后您可以从登录组件调用此提供程序,如下所示:

import { signIn } from "next-auth/react";
const result = await signIn("credentials", {
  username: <username value>,
  password: <password value>,
  redirect: <true or false for enabling redirecting after login>,
  callbackUrl: "<URL for callback>",
});

您还可以指定中间件。

更多信息:https://next-auth.js.org/

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