Express.js JWT 身份验证问题:用户令牌在本地存储中可用,但收到未经过身份验证的消息

问题描述 投票:0回答:1
我想提交评论。但在我的 verifyToken 和 verifyUser 中间件中,我没有得到该值。相反,它说虽然我在控制台中获取了令牌和用户名,但值未定义。

由于我没有在中间件中获取用户信息和令牌,因此表示我未获得授权并阻止我提交评论。 如果我从审核路径中删除 verifyToken 和 verifyUser 中间件,那么它就可以正常工作。 但如果我添加它们然后在终端中显示:

req.user: undefined Token: undefined
虽然我在前端本地存储中同时获取了令牌和用户。

我需要帮助来克服这个问题。 谢谢提前。

中间件:

const jwt = require("jsonwebtoken"); const verifyToken = (req, res, next) => { console.log("req.user:", req.user); console.log("req.params.tourId:", req.params.tourId); const token = req.cookies.accessToken; console.log("Token received in verifyToken:", token); console.log("Token:", token); if (!token) { return res.status(401).json({ success: false, message: "You are not authorized", }); } try { const user = jwt.verify(token, process.env.JWT_SECRET); console.log("Decoded User:", user); req.user = user; next(); } catch (error) { console.error("Token Verification Error:", error); res.status(401).json({ success: false, message: "Token is Invalid", }); } }; const verifyUser = (req, res, next) => { console.log("req.user:", req.user); console.log("req.params.tourId:", req.params.tourId); if ( req.user && (req.user.id === req.params.tourId || req.user.role === "admin") ) { next(); } else { return res.status(401).json({ success: false, message: "You're not authenticated", }); } }; const verifyAdmin = (req, res, next) => { if (req.user.role === "admin") { next(); } else { return res.status(401).json({ success: false, message: "You're not authorized", }); } }; module.exports = { verifyToken, verifyAdmin, verifyUser };
路线:

const express = require("express"); const router = express.Router(); const { verifyUser, verifyToken } = require("../utils/verifyToken"); const { createReview } = require("../controller/reviewController"); router.post("/:tourId", verifyToken, verifyUser, createReview); module.exports = router;
控制器:

const Review = require("../model/Review"); const Tour = require("../model/Tour"); const createReview = async (req, res) => { const tourId = req.params.tourId; const newReview = new Review({ ...req.body }); try { const savedReview = await newReview.save(); await Tour.findOneAndUpdate( { _id: tourId }, { $push: { reviews: savedReview._id } } ); res.status(200).json({ success: true, message: "Review Submitted", data: savedReview, }); } catch (error) { res.status(500).json({ success: false, message: error.message, }); console.log(error); } }; module.exports = { createReview };
TourDetails.jsx:

import React, { useEffect, useRef, useState } from "react"; import "../styles/TourDetails.css"; import { Container, Row, Col, Form, ListGroup } from "reactstrap"; import { useParams } from "react-router-dom"; import calculateAvgRating from "../utils/averageRating"; import { AiFillStar, AiOutlineStar } from "react-icons/ai"; import { MdLocationPin } from "react-icons/md"; import { HiUserGroup } from "react-icons/hi"; import { FaDollarSign } from "react-icons/fa"; import { GrLocation } from "react-icons/gr"; import { RiPinDistanceFill } from "react-icons/ri"; import avatar from "../assets/tour-images/avatar.jpg"; import Booking from "../components/Booking/Booking"; import { useSelector, useDispatch } from "react-redux"; import { getSingleTour } from "../redux/action/tourAction"; import { server } from "../server"; import axios from "axios"; import { toast } from "react-toastify"; const TourDetails = () => { const { id } = useParams(); const dispatch = useDispatch(); const reviewsMsgRef = useRef(""); const [userRating, setUserRating] = useState(0); const [user, setUser] = useState({}); const [submittedReview, setSubmittedReview] = useState(null); const [isLoggedIn, setIsLoggedIn] = useState( !!localStorage.getItem("accessToken") ); // Fetch single tour when the component mounts useEffect(() => { dispatch(getSingleTour(id)); window.scrollTo(0, 0); }, [dispatch, id]); useEffect(() => { setIsLoggedIn(!!localStorage.getItem("accessToken")); const storedUser = JSON.parse(localStorage.getItem("user")); if (storedUser) { setUser(storedUser); } console.log(user); }, []); const { tour, isLoading, error } = useSelector((state) => state.tour); if (isLoading) { return <div>Loading...</div>; } if (error) { return <div>{error}</div>; } if (!tour) { return <div>Tour not found.</div>; } const { photo, title, desc, price, reviews, address, city, distance, maxGroupSize, } = tour.data; const { totalRating, avgRating } = calculateAvgRating(reviews); const options = { day: "numeric", month: "long", year: "numeric" }; const handleStarClick = (rating) => { setUserRating(rating); }; const isRatingSelected = (rating) => rating <= userRating; const handleSubmitReview = async (e) => { e.preventDefault(); const reviewText = reviewsMsgRef.current.value; const accessToken = localStorage.getItem("accessToken"); if (!accessToken) { toast.error("Please login first to submit a review."); return; } try { const reviewObj = { username: user.name, reviewText, rating: userRating, }; const res = await axios.post(`${server}/reviews/${id}`, reviewObj, { withCredentials: true, headers: { Authorization: `Bearer ${accessToken}`, }, }); // Store the submitted review data in the component state setSubmittedReview(res.data.data); toast.success("Review submitted successfully"); console.log("Review submitted successfully:", res.data); } catch (error) { toast.error("Error submitting review"); console.log(error); } }; return ( <section> <Container> <Row> <Col lg="8"> <div className="tour_content"> <img src={photo} alt="photo" /> <div className="tour_info"> <h2>{title}</h2> <div className="d-flex align-items-center gap-5"> {avgRating > 0 ? ( <span className="tour_rating d-flex align-items-center gap-1"> <AiFillStar className="tour_icon" /> {avgRating} <span>({reviews?.length})</span> </span> ) : ( <span className="tour_rating">Not Rated</span> )} <span> <GrLocation className="tour_icon" /> {address} </span> </div> <div className="tour_extra-details d-flex align-items center"> <span> <MdLocationPin className="tour_icon" /> {city} </span> <span> <RiPinDistanceFill className="tour_icon" /> {distance} k/m </span> <span> <FaDollarSign className="tour_icon" />$ {price}/per person </span> <span> <HiUserGroup className="tour_icon" /> {maxGroupSize} people </span> </div> <h5>Description</h5> <p>{desc}</p> </div> <div className="tour_reviews mt-4"> <h4>Reviews ({reviews?.length} reviews)</h4> <Form onSubmit={handleSubmitReview}> <div className="rating_group d-flex align-items-center gap-3 mb-4"> {[1, 2, 3, 4, 5].map((rating) => ( <span key={rating} onClick={() => handleStarClick(rating)} > {isRatingSelected(rating) ? ( <AiFillStar /> ) : ( <AiOutlineStar /> )} </span> ))} </div> <div className="review_input d-flex align-items-center"> <input type="text" ref={reviewsMsgRef} placeholder="Share your thoughts" required /> <button className="btn primary_btn text-white" type="submit" > Submit </button> </div> </Form> <ListGroup className="user_reviews"> {submittedReview && ( <div className="review_item"> <img src={avatar} alt="avatar" /> <div className="w-100"> <div className="d-flex align-items-center justify-content-between"> <div> <h5>{submittedReview.username}</h5> <p> {new Date().toLocaleDateString("en-US", options)} </p> </div> <span className="d-flex align-items-center"> <AiFillStar className="review_icon" />5 </span> </div> <h6>{submittedReview.reviewText}</h6> </div> </div> )} {reviews.map((review, index) => ( <div className="review_item" key={index}> <img src={avatar} alt="avatar" /> <div className="w-100"> <div className="d-flex align-items-center justify-content-between"> <div> <h5>{review.username}</h5> <p> {new Date("08-28-2023").toLocaleDateString( "en-US", options )} </p> </div> <span className="d-flex align-items-center"> <AiFillStar className="review_icon" />5 </span> </div> <h6>Amazing Tour</h6> </div> </div> ))} </ListGroup> </div> </div> </Col> <Col lg="4"> <Booking tour={tour} avgRating={avgRating} /> </Col> </Row> </Container> </section> ); }; export default TourDetails;
    
node.js reactjs express authentication middleware
1个回答
1
投票

编辑

看起来您正在前端的

Authorization 标头上发送访问令牌,但尝试从 verifyToken

 中间件函数中的 cookie 中读取。请记住,在 axios 请求配置对象中设置的 
withCredentials
 属性不会自动发送存储在 
localStorage
 对象中的数据,它只会发送为当前浏览会话存储的相关 cookie。只要请求的 
Authorization
 标头中发送了有效的会话 jwt,以下代码就应该可以帮助您通过验证。

const verifyToken = (req, res, next) => { const authHeader = req.get("Authorization")?.split(" "); if (!authHeader || authHeader[0] !== "Bearer" || !authHeader[1]) { return res.status(401).json({ success: false, message: "You are not authorized", }); } const token = authHeader[1]; try { const user = jwt.verify(token, process.env.JWT_SECRET); req.user = user; next(); } catch (error) { console.error("Token Verification Error:", error); if(["JsonWebTokenError", "TokenExpiredError"].includes(error.name)) { return res.status(401).json({ success: false, message: "Token is Invalid", }); } res.status(500).json({ success: false, message: "Internal Server Error", }); } };


原答案:

我没有看到任何对

verifyToken

 函数的调用,该函数应该设置 
req.user
 对象,因此看起来您需要替换:

router.post("/:tourId", verifyUser, createReview);
与:

router.post("/:tourId", verifyToken, verifyUser, createReview);
这将首先调用 

verifyToken

,如果验证成功,它将设置 
req.user
 对象,然后调用下一个中间件函数,在本例中为 
verifyUser

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