javascript 使用nextauth 4.22.1验证时显示图像(jpg)

z6psavjg  于 2023-06-28  发布在  Java
关注(0)|答案(1)|浏览(93)

大家好
如标题中所提到的,我试图使用nextauth 4.22.1来显示验证图像。我使用的是新的/app目录,它位于/src目录中。有时候它是有效的,但大多数时候它不是。
下面是我的代码:\src\app\fotos\page.js:

import Link from "next/link";
import { getFolders } from "./getFolders";

export default function Fotos() {
    const folderNames = getFolders();
    return (
        <main>
            <h2>Bestellung</h2>
            <p>Hier kommen Fotos hin.</p>
            <h3>Veranstaltungen:</h3>
            <ul>
                {folderNames.map((folderName) => (
                    <li key={folderName}>
                        <Link href={`/fotos/${folderName}`}>{folderName}</Link>
                    </li>
                ))}
            </ul>
        </main>
    );
}

\src\app\fotos\getFolders.js:

import fs from "fs";
import path from "path";

export function getFolders() {
    const directoryPath = path.join(process.cwd(), "/src/app/fotos/[course]");
    const fileNames = fs.readdirSync(directoryPath);
    return fileNames.filter((e) => {
        return e !== ".gitignore" && e !== "page.js" && e !== "FotoPage.js";
    });
}

\src\app\fotos[course]\page.js:

"use client";

import { SessionProvider } from "next-auth/react";
import FotoPage from "./FotoPage";

export default function Show(props) {
    return (
        <SessionProvider>
            <FotoPage props={props} />
        </SessionProvider>
    );
}

\src\app\fotos[course]\FotoPage.js:

"use client";

import { getSession } from "next-auth/react";
import { redirect } from "next/navigation";
import Link from "next/link";
import UWImage from "@/components/UWImage";

export default async function FotoPage({ props }) {
    const session = await getSession();
    const folderListPromise = await fetch("/api/getfotofoldercontent");
    const folderList = await folderListPromise.json();
    const course = props.params.course;
    if (folderList.folderNames.includes(course)) {
        if (session?.user.name === course) {
            const fileNamesPromise = await fetch(`/api/getfotofoldercontent/${course}`);
            const fileNamesList = await fileNamesPromise.json();
            return (
                <main>
                    <h2>Fotos aus Kurs {course}</h2>
                    <div style={{ display: "flex", flexWrap: "wrap", gap: "10px", justifyContent: "center" }}>
                        {fileNamesList.fileNames.map((fileName) => (
                            <div key={fileName} style={{ textAlign: "center" }}>
                                <strong>{fileName}</strong>
                                <UWImage course={course} fileName={fileName} />
                            </div>
                        ))}
                    </div>
                </main>
            );
        } else { // not logged in as correct user
            redirect(`/api/auth/signin?callbackUrl=/fotos/${course}`);
        }
    } else {
        return (
            <main>
                <h1>
                    Den Kurs &quot;{course}&quot; gibt es nicht.{" "}
                    <Link href="/fotos" style={{ textDecoration: "underline" }}>
                        Hier
                    </Link>{" "}
                    geht es zu den Kursen.
                </h1>
            </main>
        );
    }
}

\src\components\UWImage.js:

"use client";

import Image from "next/image";

export default function UWImage({ course, fileName }) {
    return (
        <div>
            <Image
                src={`/api/getimage?folderName=${course}&fileName=${fileName}`}
                width={400}
                height={400}
                style={{ width: "auto", height: "30vh", borderRadius: "3%" }}
                alt={fileName}
            />
        </div>
    );
}

\src\app\API\getimage\route.js:

import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { NextResponse } from "next/server";
import { readFileSync } from "fs";
import { join } from "path";

export async function GET(request) {
    const url = new URL(request.url);
    const folderName = url.searchParams.get("folderName");
    const session = await getServerSession(authOptions);
    if (session && session.user.name === folderName) {
        const fileName = url.searchParams.get("fileName");
        const imagePath = join(process.cwd(), "src/app/fotos/[course]/", folderName, fileName);
        const image = readFileSync(imagePath);
        return new NextResponse(image, { "Cache-Control": "private" });
    } else {
        return new NextResponse(null, { status: 401 });
    }
}

\src\app\API\getfotofoldercontent[[...folder]]\route.js:

import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { NextResponse } from "next/server";
import { getFolders } from "@/app/fotos/getFolders";
import { getFileNames } from "@/app/fotos/getFileNames";

export async function GET(params) {
    const folderName = new URL(params.url).pathname.split("/")[3];
    if (!folderName) {
        return NextResponse.json({ folderNames: getFolders()});
    }
    const session = await getServerSession(authOptions);
    if (session && session.user.name === folderName){
        return NextResponse.json({ fileNames: getFileNames(folderName)});
    }
    return new NextResponse(null, { status: 401 });
}

\src\app\fotos\getFileNames.js:

import fs from "fs";
import path from "path";

export function getFileNames(folderName) {
    const directoryPath = path.join(process.cwd(), "/src/app/fotos/[course]", folderName);
    const fileNames = fs.readdirSync(directoryPath);
    return fileNames;
}

\src\app\API\auth\signin\page.js:

import LoginForm from "@/components/LoginForm";

export default function LoginPage() {
    return (
        <main style={{ display: "flex", justifyContent: "center" }}>
            <div className="LoginContainer">
                <br />
                <div className="LoginBackgroundImage">
                    <h1 className="LoginHeading">Login</h1>
                </div>
                <br />
                <LoginForm />
            </div>
        </main>
    );
}

\src\components\LoginForm.js:

"use client";

import { signIn } from "next-auth/react";
import { useSearchParams, useRouter } from "next/navigation";
import { useState } from "react";

export default function LoginForm() {
    const router = useRouter();
    const callbackUrl = useSearchParams().get("callbackUrl") || "/";
    let [loading, setLoading] = useState(false);
    let [course, setCourse] = useState(callbackUrl.split("/").pop());
    let [password, setPassword] = useState("");
    const [error, setError] = useState("");

    async function onSubmit(e) {
        e.preventDefault();
        try {
            setLoading(true);
            const res = await signIn("credentials", {
                redirect: false,
                courseName: course,
                password: password,
                callbackUrl
            });
            setLoading(false);
            if (!res?.error) {
                setError("");
                router.push(callbackUrl);
            } else {
                setError("falsches Passwort");
            }
        } catch (error) {
            setLoading(false);
            setError(error);
        }
    }

    const handleChange = (event) => {
        event.preventDefault();
        const { name, value } = event.target;
        if (name === "course") {
            setCourse(value);
        } else if (name === "password") {
            setPassword(value);
        }
    };

    return (
        <form onSubmit={onSubmit}>
            {error ? <strong className="w3-text-red">{error}</strong> : ""}
            <div className="mb-6">
                <input
                    style={{ margin: 5 }}
                    required
                    type="text"
                    className="input-box"
                    name="course"
                    value={course}
                    onChange={handleChange}
                    placeholder="KURS32XYZ"
                />
            </div>
            <div className="mb-6">
                <input
                    style={{ margin: 5 }}
                    required
                    type="password"
                    className="input-box"
                    name="password"
                    value={password}
                    onChange={handleChange}
                    placeholder="Passwort"
                />
            </div>
            <button style={{ margin: 5, padding: 2 }} type="submit" disabled={loading}>
                {loading ? "lädt..." : "Anmelden"}
            </button>
        </form>
    );
}

\src\app\API\auth[...nextauth]\route.js:

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

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

\src\lib\auth.js:

import CredentialsProvider from "next-auth/providers/credentials";

// TODO: replace with real passwords
const passwordList = {
    "5CR33N5H0T5": "12345",
    W4LLP4P3R5: "54321",
    FR4NC3: "france",
    QW3RTZ: "qwertz",
    V1T450L: "vitasol",
    "3RINN3RUNG": "erinnerung",
    H4LL0W: "halloween",
    MILRI4: "milria"
};

function isCorrectCredentials(credentials) {
    return passwordList[credentials.courseName] === credentials.password;
}

export const authOptions = {
    session: {
        strategy: "jwt"
    },
    providers: [
        CredentialsProvider({
            name: "credentials",
            credentials: {
                courseName: {
                    label: "Kursname",
                    type: "text",
                    placeholder: "KURS32XYZ"
                },
                password: { label: "Passwort", type: "password" }
            },
            async authorize(credentials) {
                return isCorrectCredentials(credentials) ? { id: "1", name: credentials.courseName } : null;
            }
        })
    ],
    pages: {
        signIn: "api/auth/signin"
    }
};

\src\app\layout.js:

import "./globals.css";
import "./w3.css";
import { Inter } from "next/font/google";
import Header from "@/components/Header";
import Footer from "@/components/Footer";

const inter = Inter({ subsets: ["latin"] });

export const metadata = {
    title: "AquaFotos - Unterwasser-Bilder",
    description: "Unterwasser-Fotos in bester Qualität"
};

export default function RootLayout({ children }) {
    return (
        <html lang="de">
            <body className={inter.className}>
                <Header />
                {children}
                <Footer />
            </body>
        </html>
    );
}

\package.json:

{
  "name": "aquafotos.com",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "eslint": "8.43.0",
    "eslint-config-next": "^12.0.4",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-jsx-a11y": "^6.5.1",
    "eslint-plugin-react": "^7.25.3",
    "eslint-plugin-react-hooks": "^4.6.0",
    "next": "^13.4.8-canary.2",
    "next-auth": "^4.22.1",
    "react": "18.2.0",
    "react-dom": "18.2.0"
  }
}

希望我没忘了什么重要的事。我认为,页眉和页脚的内容与问题无关,但如果我错了,我也可以分享内容。错误,我经常看到的是,当我试图看到一个文件夹的图像(例如W 4LLP 4P 3R 5,我试图访问http://localhost:3000/fotos/W 4LLP 4P 3R 5),浏览器试图一遍又一遍地获取会话。我可以在开发人员控制台中看到,cookie“next-auth.session-token”的值一直在变化。在vs代码的本地控制台中,我一次又一次地收到消息(node:28917) ExperimentalWarning: stream/web is an experimental feature. This feature could change at any time(“(node:28917)”中的数字正在变化)。在浏览器控制台中,我有时会收到以下错误:

Uncaught (in promise) TypeError: param is undefined
    FotoPage FotoPage.js:8
    describeNativeComponentFrame react-dom.development.js:2534
    describeFunctionComponentFrame react-dom.development.js:2629
    describeFiber react-dom.development.js:2708
    getStackByFiberInDevAndProd react-dom.development.js:2727
    createCapturedValueAtFiber react-dom.development.js:14060
    throwException react-dom.development.js:14550
    throwAndUnwindWorkLoop react-dom.development.js:24676
    renderRootConcurrent react-dom.development.js:24291
    performConcurrentWorkOnRoot react-dom.development.js:23343
    workLoop scheduler.development.js:261
    flushWork scheduler.development.js:230
    performWorkUntilDeadline scheduler.development.js:537
    EventHandlerNonNull* scheduler.development.js:575
    <anonymous> scheduler.development.js:638
    NextJS 4
    <anonymous> index.js:6
    NextJS 4
    <anonymous> react-dom.development.js:27
    <anonymous> react-dom.development.js:36657
    NextJS 4
    <anonymous> index.js:37
    NextJS 4
    <anonymous> client.js:3
    NextJS 4
    <anonymous> app-index.js:14
    NextJS 4
    <anonymous> app-next-dev.js:8
    appBootstrap app-bootstrap.js:58
    loadScriptsInSequence app-bootstrap.js:23
    appBootstrap app-bootstrap.js:57
    <anonymous> app-next-dev.js:7
    NextJS 7
FotoPage.js:8:39

但在某些情况下,它只是工作正常。上面的错误表明,当调用http://localhost:3000/fotos/W 4LLP 4P 3R 5(例如)时,参数未定义,但我不知道这是怎么回事。预期行为:尝试获得会话。如果我以正确名称(文件夹名称,在本例中为“W 4LLP 4P 3R 5”)的用户身份登录,则显示文件夹包含的图像。如果我没有登录,或者登录的用户不正确,请重定向到登录页面。登录后,重定向回callbackUrl。您可以尝试在每个图像文件夹中执行此操作。只需将文件夹放在\src\app\fotos[course]中,并将密码输入放在\src\lib\auth.js中。也许我想得太复杂了,我想达到的目标要容易得多。如果有人有主意,我很感激。

lmyy7pcs

lmyy7pcs1#

如果其他人也遇到同样的问题:我通过以下更改使其可靠地工作:
在\src\app\fotos[course]\FotoPage.js中:

  • 用useRouter().push-method替换next/navigation中的redirect-method
  • 删除包围JSX的-标记
"use client";

import { getSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import UWImage from "@/components/UWImage";

export default async function FotoPage({ props }) {
    const router = useRouter();
    const session = await getSession();
    const folderListPromise = await fetch("/api/getfotofoldercontent");
    const folderList = await folderListPromise.json();
    const course = props.params.course;
    if (folderList.folderNames.includes(course)) {
        if (session?.user.name === course) {
            const fileNamesPromise = await fetch(`/api/getfotofoldercontent/${course}`);
            const fileNamesList = await fileNamesPromise.json();
            return (
                <>
                    <h2>Fotos aus Kurs {course}</h2>
                    <div style={{ display: "flex", flexWrap: "wrap", gap: "10px", justifyContent: "center" }}>
                        {fileNamesList.fileNames.map((fileName) => (
                            <div key={fileName} style={{ textAlign: "center" }}>
                                <strong>{fileName}</strong>
                                <UWImage course={course} fileName={fileName} />
                            </div>
                        ))}
                    </div>
                </>
            );
        } else {
            // not logged in as correct user
            router.push(`/api/auth/signin?callbackUrl=/fotos/${course}`);
        }
    } else {
        return (
            <h1>
                Den Kurs &quot;{course}&quot; gibt es nicht.{" "}
                <Link href="/fotos" style={{ textDecoration: "underline" }}>
                    Hier
                </Link>{" "}
                geht es zu den Kursen.
            </h1>
        );
    }
}

在\src\app\fotos[course]\page.js中:

  • 用-tag包围-tag
"use client";

import { SessionProvider } from "next-auth/react";
import FotoPage from "./FotoPage";

export default function Show(props) {
    return (
        <SessionProvider>
            <main>
                <FotoPage props={props} />
            </main>
        </SessionProvider>
    );
}

在\src\app\fotos\page.js中:

  • 将prefetch={false}添加到-标记
import Link from "next/link";
import { getFolders } from "./getFolders";

export default function Fotos() {
    const folderNames = getFolders();
    return (
        <main>
            <h2>Bestellung</h2>
            <p>Hier kommen Fotos hin.</p>
            <h3>Veranstaltungen:</h3>
            <ul>
                {folderNames.map((folderName) => (
                    <li key={folderName}>
                        <Link href={`/fotos/${folderName}`} prefetch={false}>{folderName}</Link>
                    </li>
                ))}
            </ul>
        </main>
    );
}

相关问题