You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MERN Stack中JWT Admin权限验证返回403 Forbidden问题

问题:MERN Stack中JWT Admin授权返回403 Forbidden

我正在基于MERN Stack优化网站,已实现JWT授权验证。使用数据库中标记为admin的账号通过Postman发送请求时,收到了我在中间件中定义的403 Forbidden提示(用于拦截非admin用户操作)。我仅将该中间件添加到/users路由做测试,但JWT并未正常生效,问题依旧存在。

相关代码

middleware.js

import jwt from 'jsonwebtoken';

export const authMiddleware = (req, res, next) => {
    const token = req.headers['authorization']?.split(' ')[1]; // 从Authorization头获取token

    if (!token) {
        return res.status(401).json({ message: 'Unauthorized' });
    }

    jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decodedToken) => {
        if (err) {
            console.log(err);
            return res.status(400).json({ message: 'Invalid token' });
        }

        if (decodedToken.usertype === 'ADMIN') {
            // 若为admin则继续执行
            next();
        } else {
            // 无admin权限则拦截
            return res.status(403).json({ message: 'Forbidden' });
        }
    });
};

adminRouter.js

import express from 'express';
import mongoose from 'mongoose';
import AdminModel from '../models/adminModel.js';
import bcrypt from 'bcryptjs';
import CourseModel from '../models/courseModel.js';
import UserModel from '../models/userModel.js';
import ActivityModel from '../models/activityModel.js';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
import { authMiddleware } from '../middlewares/middleware.js';

const router = express.Router();
dotenv.config();

router.post('/signup', async (req, res) => {
    try {
        const { fullName, password, phoneNumber, email } = req.body;
        const adminExist = await AdminModel.findOne({ email })
        if (adminExist)
            return res.status(400).json({ message: 'admin already exist' })

        const hashedPassword = await bcrypt.hash(password, 15)

        const createdUser = await AdminModel.create({
            fullName,
            email,
            password: hashedPassword,
            phoneNumber,
            usertype: "ADMIN"
        })
        return res.status(201).json(createdUser);
    }
    catch (error) {
        console.log(error)
        return res.json({ message: 'admin oluşturulamadı' })
    }
});

router.post('/signin', async (req, res) => {
    try {
        const { email, password } = req.body;
        const admin = await AdminModel.findOne({ email });

        if (!admin) {
            return res.status(400).json({ message: "admin yok" });
        }

        const isPasswordCorrect = await bcrypt.compare(password, admin.password);

        if (!isPasswordCorrect) {
            return res.status(400).json({ message: "wrong password" });
        }

        const accessToken = jwt.sign(
            { adminId: admin._id },
            process.env.ACCESS_TOKEN_SECRET,
            { expiresIn: '15m' }
        );
        
        const refreshToken = jwt.sign(
            { email: admin.email, password: admin.password },
            process.env.REFRESH_TOKEN_SECRET
        );
        return res.status(200).json({
            admin,
            accessToken,
            refreshToken,
            message: "kimlik doğrulama başarılı"
        });
    } catch (error) {
        return res.status(400).json({ message: error.message });
    }
});

// 以下省略其他路由代码...
router.get('/users', authMiddleware, async (req, res) => {
    try {
        const users = await UserModel.find();
        return res.status(200).json(users);
    } catch (error) {
        return res.status(500).json({ message: error.message });
    }
});

// 其他路由代码...
export default router;

解决方案

核心问题

生成accessToken时未将usertype字段存入JWT payload,导致中间件解析token时无法获取decodedToken.usertype,因此判定为非admin用户返回403。

修复步骤

  1. 修改signin接口的accessToken生成逻辑,加入usertype字段:
const accessToken = jwt.sign(
    { adminId: admin._id, usertype: admin.usertype }, // 添加usertype到payload
    process.env.ACCESS_TOKEN_SECRET,
    { expiresIn: '15m' }
);
  1. 可选调试优化:在中间件中打印解析后的token,确认字段是否存在:
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decodedToken) => {
    if (err) {
        console.log(err);
        return res.status(400).json({ message: 'Invalid token' });
    }
    console.log('解析后的Token:', decodedToken); // 调试用日志
    if (decodedToken.usertype === 'ADMIN') {
        next();
    } else {
        return res.status(403).json({ message: 'Forbidden' });
    }
});
  1. 安全优化:refreshToken中不要存入password哈希值,仅保留必要信息:
const refreshToken = jwt.sign(
    { email: admin.email, usertype: admin.usertype }, // 移除password字段
    process.env.REFRESH_TOKEN_SECRET
);

内容的提问来源于stack exchange,提问作者Meliqe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 05:18:09