如何从 URL 获取令牌?

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

目前,我有一个通过用户电子邮件发送的身份验证 URL,其中包含以下令牌:

"http://localhost:5173/activation/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWZkNmM0OTMyOWI4YzhjNjFjMjJiY2YiLCJpYXQiOjE3MTExMDcxNDUsImV4cCI6MTcxMTE5MzU0NX0.BLkEkRtRxYk3uKKobWRuD0rsf7qob35l5-OmdJfRqyU"

当前的问题是我正在尝试使用 React-Router-DOM 的

useParams()
获取令牌(“activation/”之后的字符串)。如果该字符串不包含像
"."
这样的字符,那么我会得到
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
但是如果完整的标记就像上面的
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWZkNmM0OTMyOWI4YzhjNjFjMjJiY2YiLCJpYXQiOjE3MTExMDcxNDUsImV4cCI6MTcxMTE5MzU0NX0.BLkEkRtRxYk3uKKobWRuD0rsf7qob35l5-OmdJfRqyU"
这样,那么我就不会得到完整的标记值。

路线/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;

控制器/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" });
  }
};

⬇️前端代码

应用程序.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;

激活.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;

我不知道问题从何而来。我正在尝试使用

"activation/"
获取令牌(
useParams()
之后的字符串),但如果该字符串不包含像
"."
这样的字符,那么我会得到(例如
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
),但如果完整令牌如上,我不明白。我有
console.log(activation_token)
取自
useParams

javascript reactjs url react-router-dom nodemailer
1个回答
0
投票

正如您在此处发现的 some 字符在 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
挂钩访问查询字符串中的标记搜索参数。

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;
© www.soinside.com 2019 - 2024. All rights reserved.