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

如何实现仅允许发布者编辑/删除MongoDB帖子(Express.js)

帖子权限控制实现方案(Express.js + MongoDB)

需求背景

我的应用基于MongoDB实现了帖子的增删改查(CRUD)功能,但存在权限漏洞:任意登录用户均可修改或删除任意帖子。需要实现仅允许发布该帖子的用户对其执行CRUD操作的权限控制,使用Express.js完成。

客户端UserProvider.js代码

import axios from "axios";
import UserContext from "./UserContext";
import React from "react";

export const UserProvider = (props) => {

    const baseUrl = "http://localhost:3000/api/users";

    function createUser(username, password, firstName, lastName) {       
        let user = { username, password, firstName, lastName };
        // changes here
        return axios.post(baseUrl, user)
            .then(response => {
                return new Promise(resolve => resolve(response.data));
            }
        );
    }

    function signInUser(username, password) {
        let user = { username, password };

        return axios.post(`${baseUrl}/login`, user)
            .then(response => {
                localStorage.setItem('myCoffeeToken', response.data.token)
                return new Promise(resolve => resolve(response.data));
            }
        );
    }

    return (
        <UserContext.Provider value={{
            createUser,
            signInUser
        }}>
            { props.children }
        </UserContext.Provider>
    )
}

权限控制实现步骤

1. 给帖子集合添加发布者关联字段

MongoDB的posts集合必须新增author字段,用来存储发布该帖子的用户_id(ObjectId类型)。示例帖子模型:

// models/Post.js
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User', // 关联用户模型
    required: true
  },
  // 其他业务字段...
});

module.exports = mongoose.model('Post', postSchema);

2. 编写身份验证中间件

先实现解析JWT令牌的中间件,提取当前登录用户的ID并挂载到请求对象上:

// middleware/auth.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');

module.exports = async (req, res, next) => {
  // 从请求头提取令牌
  const token = req.header('Authorization')?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ message: '未授权,请先登录' });
  }

  try {
    // 验证并解码令牌
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    // 查找用户并挂载到req对象(排除密码字段)
    req.user = await User.findById(decoded.userId).select('-password');
    next();
  } catch (err) {
    res.status(401).json({ message: '令牌无效' });
  }
};

3. 编写帖子权限校验中间件

创建中间件检查当前登录用户是否为帖子的发布者:

// middleware/postAuth.js
const Post = require('../models/Post');

module.exports = async (req, res, next) => {
  try {
    // 根据URL参数获取目标帖子
    const post = await Post.findById(req.params.id);
    if (!post) {
      return res.status(404).json({ message: '帖子不存在' });
    }

    // 校验用户ID与帖子作者ID是否匹配
    if (post.author.toString() !== req.user._id.toString()) {
      return res.status(403).json({ message: '无权限操作此帖子' });
    }

    // 校验通过,将帖子挂载到req对象供后续路由使用
    req.post = post;
    next();
  } catch (err) {
    res.status(500).json({ message: '服务器错误' });
  }
};

4. 改造帖子路由,添加权限控制

在帖子的CRUD路由中,结合上面两个中间件实现权限控制:

// routes/posts.js
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const postAuth = require('../middleware/postAuth');
const Post = require('../models/Post');

// 创建帖子 - 仅登录用户可操作,自动关联当前用户为作者
router.post('/', auth, async (req, res) => {
  try {
    const post = new Post({
      ...req.body,
      author: req.user._id // 绑定当前登录用户ID
    });
    await post.save();
    res.status(201).json(post);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

// 获取单个帖子(公开访问,如需仅作者查看可添加postAuth中间件)
router.get('/:id', async (req, res) => {
  try {
    const post = await Post.findById(req.params.id).populate('author', 'username firstName');
    if (!post) {
      return res.status(404).json({ message: '帖子不存在' });
    }
    res.json(post);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// 修改帖子 - 需登录+权限校验
router.put('/:id', auth, postAuth, async (req, res) => {
  try {
    // 直接使用postAuth中间件挂载的req.post进行更新
    Object.assign(req.post, req.body);
    await req.post.save();
    res.json(req.post);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

// 删除帖子 - 需登录+权限校验
router.delete('/:id', auth, postAuth, async (req, res) => {
  try {
    await req.post.remove();
    res.json({ message: '帖子已删除' });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

module.exports = router;

5. 客户端请求时携带令牌

确保客户端在调用帖子接口时,将myCoffeeToken放入请求头的Authorization字段,可通过axios拦截器实现自动携带:

// 在UserProvider中添加带拦截器的axios实例
const api = axios.create({
  baseURL: 'http://localhost:3000/api',
});

// 请求拦截器:自动在请求头添加令牌
api.interceptors.request.use(config => {
  const token = localStorage.getItem('myCoffeeToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 后续使用api实例发起帖子相关请求即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:10:29