目前,我有一个通过用户邮箱发送的身份验证URL,其中包含以下令牌:"http://localhost:5173/activation/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWZkNmM0OTMyOWI4YzhjNjFjMjJiY2YiLCJpYXQiOjE3MTExMDcxNDUsImV4cCI6MTcxMTE5MzU0NX0.BLkEkRtRxYk3uKKobWRuD0rsf7qob35l5-OmdJfRqyU"

现在的问题是,我试图使用useParams()%的Reaction-Router-DOM来获取令牌("active/"之后的字符串).如果该字符串不包括像"."这样的字符,那么我会得到"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",但是如果一个完整的令牌像上面的"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWZkNmM0OTMyOWI4YzhjNjFjMjJiY2YiLCJpYXQiOjE3MTExMDcxNDUsImV4cCI6MTcxMTE5MzU0NX0.BLkEkRtRxYk3uKKobWRuD0rsf7qob35l5-OmdJfRqyU"一样,那么我就不会得到完整的令牌值.

Routes/auth.js

const express = require("express");

const authController = require("../controllers/auth");

const router = express.Router();

router.post("/signup", authController.postSignup);
router.post("/activation", authController.activateAccount);

module.exports = router;

controllers/auth.js

const bcrypt = require("bcrypt");
const User = require("../models/user");
const { v4: uuidv4 } = require("uuid");
const jwt = require("jsonwebtoken");
const { getAuth } = require("firebase-admin/auth");

const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; // regex for email
const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/; // regex for password
const sendMail = require("../utils/sendMail");

const generateUserName = async (email) => {
  let username = email.split("@")[0];
  const isUsernameExists = await User.exists({
    "personal_info.username": username
  });

  if (isUsernameExists) {
    username += uuidv4().substring(0, 5);
  }

  return username;
};

const fortmatDataToSend = (user) => {
  const access_token = jwt.sign(
    { id: user._id },
    process.env.SECRET_ACCESS_KEY,
    { expiresIn: "1h" }
  );
  return {
    access_token,
    _id: user._id,
    profile_img: user.personal_info.profile_img,
    username: user.personal_info.username,
    fullname: user.personal_info.fullname
  };
};

exports.postSignup = async (req, res) => {
  const { fullname, email, password } = req.body;

  try {
    if (fullname.length < 3) {
      return res
        .status(403)
        .json({ error: "Fullname must be at least 3 letters long !" });
    }

    if (!email.length) {
      return res.status(403).json({ error: "Enter Email" });
    }

    if (!emailRegex.test(email)) {
      return res.status(403).json({ error: "Invalid email address" });
    }

    if (!passwordRegex.test(password)) {
      return res.status(403).json({
        error:
          "Password should be 6 to 20 characters long with a numeric, 1 lowercase and 1 uppercase letters!"
      });
    }

    const existingUser = await User.findOne({ email: email }).exec();

    if (existingUser) {
      return res.status(409).json({ error: "Email is already registered" });
    }

    const hashedPassword = await bcrypt.hash(password, 12);

    const username = await generateUserName(email);

    const user = new User({
      personal_info: {
        fullname,
        email,
        password: hashedPassword,
        username,
        active: false
      }
    });

    const activation_token = jwt.sign(
      { userId: user._id },
      process.env.SECRET_ACCESS_KEY,
      { expiresIn: "1d" }
    );

    const activationLink = `${process.env.CLIENT}/activation/${activation_token}`;

    await sendMail({
      email: email,
      subject: "Activate Your Account",
      message: `
        <p>Hello ${fullname} 👋, Please click following link to active your account ⬇️</p>
        <a href="${activationLink}">Verify Your Email</a>
    `
    });
    await user.save();
    return res.status(200).json({
      status:
        "Registration successful. Please check your email for activation link."
    });
  } catch (err) {
    if (err.code === 11000) {
      return res.status(409).json({ error: "Email is already registered!" });
    }
    return res.status(500).json({ error: err.message });
  }
};

exports.activateAccount = async (req, res) => {
  try {
    const { activation_token } = req.body;
    const decodedToken = jwt.verify(
      activation_token,
      process.env.SECRET_ACCESS_KEY
    );
    const userId = decodedToken.userId;

    const user = await User.findById(userId);

    if (!user) {
      return res.status(404).json({ error: "User not found" });
    }

    if (user.personal_info.active) {
      return res.status(400).json({ error: "Account already activated" });
    }

    user.personal_info.active = true;
    await user.save();

    return res.status(200).json({ message: "Account activated successfully" });
  } catch (error) {
    if (error.name === "TokenExpiredError") {
      return res.status(400).json({ error: "Activation token expired" });
    } else if (error.name === "JsonWebTokenError") {
      return res.status(400).json({ error: "Invalid activation token" });
    }
    return res.status(500).json({ error: "Internal server error" });
  }
};

服务器前端代码️

App.js

import { Route, Routes } from "react-router-dom";
import Navbar from "./components/navbar.component";
import UserAuthForm from "./pages/userAuthForm";
import UserContextProvider from "../context/user-context";
import Editor from "./pages/editor";
import Activation from "./pages/Activation";

const App = () => {
  return (
    <UserContextProvider>
      <Routes>
        <Route path="/" element={<Navbar />}>
          <Route path="signin" element={<UserAuthForm type="signin" />}></Route>
          <Route path="signup" element={<UserAuthForm type="signup" />}></Route>
        </Route>
        <Route path="/editor" element={<Editor />} />
        <Route path="/activation/:activation_token" element={<Activation />} />
      </Routes>
    </UserContextProvider>
  );
};

export default App;

Activation.jsx

import axios from "axios";
import { useEffect, useState } from "react";
import { useLocation, useParams } from "react-router-dom";

const Activation = () => {
  const { activation_token } = useParams();
  const [error, setError] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (activation_token) {
      const activateAccount = async () => {
        try {
          await axios.post(
            `http://localhost:3000/activation/${activation_token}`
          );
        } catch (error) {
          setError(true);
          setLoading(false);
        }
      };
      activateAccount();
    }
  }, [activation_token]);

  return (
    <div>
      {loading ? (
        <p>Loading...</p>
      ) : error ? (
        <div>
          <h1>Activation Error</h1>
          <p>
            There was an error activating your account. Please try again later.
          </p>
        </div>
      ) : (
        <div>
          <h1>Account Activated Successfully</h1>
          <p>Your account has been successfully activated.</p>
        </div>
      )}
    </div>
  );
};

export default Activation;

我不知道问题出在哪里.我试图使用useParams()获取令牌("activation/"之后的字符串),但如果该字符串不包括"."这样的字符,那么我就得到(例如"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"),但如果完整的令牌是如上所述,我就没有得到它.我从useParams中提取了console.log(activation_token).

推荐答案

正如您在这里发现的,some个字符在URL的路径部分是无效的,所以当您使用一个令牌值作为路径段时,并不是所有的都是可读的.您可以清理生成的令牌,使其仅包含有效的URL路径字符,也可以将令牌移动到搜索参数.

示例URL:"http://localhost:5173/activation?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWZkNmM0OTMyOWI4YzhjNjFjMjJiY2YiLCJpYXQiOjE3MTExMDcxNDUsImV4cCI6MTcxMTE5MzU0NX0.BLkEkRtRxYk3uKKobWRuD0rsf7qob35l5-OmdJfRqyU"

const App = () => {
  return (
    <UserContextProvider>
      <Routes>
        <Route path="/" element={<Navbar />}>
          <Route path="signin" element={<UserAuthForm type="signin" />}></Route>
          <Route path="signup" element={<UserAuthForm type="signup" />}></Route>
        </Route>
        <Route path="/editor" element={<Editor />} />
        <Route path="/activation" element={<Activation />} />
      </Routes>
    </UserContextProvider>
  );
};

使用useSearchParams挂钩访问queryString中的令牌搜索参数.

import axios from "axios";
import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";

const Activation = () => {
  const [searchParams] = useSearchParams();
  const [error, setError] = useState(false);
  const [loading, setLoading] = useState(true);

  const activation_token = searchParams.get("token");

  useEffect(() => {
    if (activation_token) {
      const activateAccount = async () => {
        setLoading(true);
        try {
          await axios.post(
            `http://localhost:3000/activation/${activation_token}`
          );
        } catch (error) {
          setError(true);
        } finally {
          setLoading(false);
        }
      };

      activateAccount();
    }
  }, [activation_token]);

  return (
    <div>
      {loading ? (
        <p>Loading...</p>
      ) : error ? (
        <div>
          <h1>Activation Error</h1>
          <p>
            There was an error activating your account. Please try again later.
          </p>
        </div>
      ) : (
        <div>
          <h1>Account Activated Successfully</h1>
          <p>Your account has been successfully activated.</p>
        </div>
      )}
    </div>
  );
};

export default Activation;

Javascript相关问答推荐

为什么JavaScript双边字符串文字插值不是二次的?

我无法在NightWatch.js测试中获取完整的Chrome浏览器控制台日志(log)

被CSS优先级所迷惑

将状态向下传递给映射的子元素

将旋转的矩形zoom 到覆盖它所需的最小尺寸&S容器

如何用拉威尔惯性Vue依赖下拉?

使用GraphQL查询Uniswap的ETH价格

在JS中拖放:检测文件

如何在ASP.NET项目中使用Google Chart API JavaScript将二次轴线值格式化为百分比

Puppeteer上每页的useProxy返回的不是函数/构造函数

元素字符串长度html

Vaadin定制组件-保持对javascrip变量的访问

try 使用PM2在AWS ubuntu服务器上运行 node 进程时出错

Next.js中的服务器端组件列表筛选

如何找到带有特定文本和测试ID的div?

使用Perl Selify::Remote::Driver执行Java脚本时出错

使用Library chart.js在一个带有两个y轴的图表中绘制两个数据集

如何使用fltter_js将对象作为参数传递给javascrip?

如何用react组件替换dom元素?

如何在不将整个文件加载到内存的情况下,在Node.js中实现Unix粘贴命令?