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

passportjs保护页面实现公开分享的最优方案及最小重构方法

Node.js Passport 项目公开页面功能实现方案

核心思路

在不改动现有业务逻辑的前提下,通过新增两类中间件实现权限解耦,同时扩展数据模型的字段和返回方法,兼顾当前需求和后续扩展性。

具体实现步骤

  • 第一步:扩展现有数据模型,新增公开标识字段和公开数据过滤方法,默认值设为私有,完全兼容原有业务逻辑
  • 第二步:新增可选认证中间件,复用现有Passport的认证逻辑,不强制要求用户登录,仅对有有效凭证的请求挂载用户信息
  • 第三步:新增资源级权限校验中间件,统一处理公开/私有资源的访问规则,避免在每个路由重复编写权限逻辑
  • 第四步:新增公开资源的访问路由,组合使用上述两个中间件,原有私有路由完全保留原有ensureAuth逻辑无需改动

代码示例

1. 扩展Blend数据模型

const blendSchema = new mongoose.Schema({
  // 原有所有字段保持不变
  isPublic: {
    type: Boolean,
    default: false, // 默认私有,不影响已有页面的访问规则
    required: true
  }
})

// 新增实例方法:过滤返回给公开访问的非敏感字段
blendSchema.methods.getPublicFields = function() {
  // 自行调整要过滤的敏感字段,比如所属用户ID、未发布内容等
  const { user, unpublishedContent, ...publicFields } = this.toObject()
  return publicFields
}

const Blend = mongoose.model('Blend', blendSchema)

2. 新增可选认证中间件

// 可选认证:不强制登录,有有效会话就挂载用户信息,没有也放行
const optionalAuth = (req, res, next) => {
  passport.authenticate('session', (err, user) => {
    if (err) return next(err)
    if (user) req.user = user
    next()
  })(req, res, next)
}

3. 新增Blend资源权限校验中间件

/**
 * 校验Blend资源访问权限
 * @param {'read' | 'write'} requiredPerm 所需权限
 */
const checkBlendAccess = (requiredPerm = 'read') => async (req, res, next) => {
  const blend = await Blend.findById(req.params.id)
  if (!blend) return res.status(404).send('页面不存在')

  // 挂载资源和权限标识到请求对象,后续路由无需重复查询
  req.blend = blend
  req.isOwner = req.user?.[0]?._id?.toString() === blend.user.toString()

  // 写权限(编辑、删除)必须要求是资源所有者
  if (requiredPerm === 'write') {
    return req.isOwner ? next() : res.status(403).send('无操作权限')
  }

  // 读权限:公开页面 或 所有者访问 直接放行
  if (blend.isPublic || req.isOwner) {
    return next()
  }

  // 私有页面非所有者访问返回403
  return res.status(403).send('该页面为私有页面')
}

4. 新增公开访问路由,原有路由无需改动

// 新增公开可读的页面路由
router.get('/:id', optionalAuth, checkBlendAccess('read'), async (req, res) => {
  const { blend, isOwner } = req
  // 所有者返回全量数据,公开访问返回过滤后的非敏感数据
  const returnData = isOwner ? blend : blend.getPublicFields()
  res.send(returnData)
})

// 原有路由完全保留,无需任何改动
router.post('/', ensureAuth, async (req, res) => {
  const newBlend = new Blend({ ...req.body, user: req.user[0]._id })
  // 原有逻辑不变
  try {
    await newBlend.save()
    res.send(newBlend)
  } catch (err) {
    if (err.name === 'ValidationError') {
      return res.status(400).send(
        Object.values(err.errors)
          .map((val) => val.message)
          .toString()
      )
    }
    res.status(400).send(err.message)
  }
})

后续扩展建议

如果后续需要支持更多类型资源的公开分享,可以把checkBlendAccess封装为通用的资源校验中间件,传入模型类型参数即可复用,无需重复编写权限逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 10:39:04