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

如何在Node.js Sequelize中关联交易模型与用户表实现数据隔离?

解决方案

1. 修正关联关系:一对多才是正确选择

你考虑的一对一关系不合适,一个用户必然会有多个交易记录,所以User(用户)和 Transaction(交易)是一对多的关联:

  • 在Transaction模型中添加userId字段(作为关联User表id的外键)
  • 通过Sequelize定义两者的关联关系:

User模型代码示例:

const { Model, DataTypes } = require('sequelize');
const sequelize = require('../config/database');

class User extends Model {}
User.init({
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  username: {
    type: DataTypes.STRING,
    unique: true,
    allowNull: false
  },
  password: {
    type: DataTypes.STRING,
    allowNull: false
  }
}, { sequelize, modelName: 'user' });

// 定义关联:一个用户拥有多个交易
User.hasMany(require('./Transaction'), { foreignKey: 'userId' });

module.exports = User;

Transaction模型代码示例:

const { Model, DataTypes } = require('sequelize');
const sequelize = require('../config/database');

class Transaction extends Model {}
Transaction.init({
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  concept: {
    type: DataTypes.STRING,
    allowNull: false
  },
  amount: {
    type: DataTypes.FLOAT,
    allowNull: false
  },
  category: {
    type: DataTypes.STRING,
    allowNull: false
  },
  userId: { // 添加外键,关联用户表
    type: DataTypes.INTEGER,
    references: {
      model: 'users', // 对应User模型生成的表名(Sequelize默认复数形式)
      key: 'id'
    },
    allowNull: false
  }
}, { sequelize, modelName: 'transaction' });

// 定义关联:每个交易属于一个用户
Transaction.belongsTo(require('./User'), { foreignKey: 'userId' });

module.exports = Transaction;

2. 注册/登录核心实现(JWT + bcrypt)

注册时需要加密存储密码,登录时验证密码并返回JWT令牌,步骤如下:

安装依赖

npm install bcryptjs jsonwebtoken

注册路由示例

const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/User');

router.post('/register', async (req, res) => {
  try {
    const { username, password } = req.body;
    // 检查用户名是否已存在
    const existingUser = await User.findOne({ where: { username } });
    if (existingUser) {
      return res.status(400).json({ message: '用户名已存在' });
    }
    // 加密密码
    const hashedPassword = await bcrypt.hash(password, 10);
    // 创建用户
    const user = await User.create({ username, password: hashedPassword });
    // 返回JWT令牌
    const token = jwt.sign({ userId: user.id }, '你的密钥', { expiresIn: '7d' });
    res.status(201).json({ token, userId: user.id });
  } catch (err) {
    res.status(500).json({ message: '服务器错误' });
  }
});

登录路由示例

router.post('/login', async (req, res) => {
  try {
    const { username, password } = req.body;
    const user = await User.findOne({ where: { username } });
    if (!user) {
      return res.status(400).json({ message: '用户名或密码错误' });
    }
    // 验证密码
    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
      return res.status(400).json({ message: '用户名或密码错误' });
    }
    // 返回JWT令牌
    const token = jwt.sign({ userId: user.id }, '你的密钥', { expiresIn: '7d' });
    res.json({ token, userId: user.id });
  } catch (err) {
    res.status(500).json({ message: '服务器错误' });
  }
});

3. 路由保护:确保用户仅能访问自己的交易

创建JWT验证中间件,解析令牌并获取当前用户ID:

const jwt = require('jsonwebtoken');

const authMiddleware = (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  if (!token) {
    return res.status(401).json({ message: '未授权,请先登录' });
  }
  try {
    const decoded = jwt.verify(token, '你的密钥');
    req.userId = decoded.userId; // 将用户ID挂载到请求对象上
    next();
  } catch (err) {
    res.status(401).json({ message: '无效的令牌' });
  }
};

module.exports = authMiddleware;

在交易相关路由中使用该中间件,并过滤当前用户的交易数据:

const express = require('express');
const router = express.Router();
const Transaction = require('../models/Transaction');
const authMiddleware = require('../middleware/auth');

// 获取当前用户的所有交易
router.get('/transactions', authMiddleware, async (req, res) => {
  try {
    const transactions = await Transaction.findAll({ where: { userId: req.userId } });
    res.json(transactions);
  } catch (err) {
    res.status(500).json({ message: '服务器错误' });
  }
});

// 添加交易(自动关联当前用户)
router.post('/transactions', authMiddleware, async (req, res) => {
  try {
    const { concept, amount, category } = req.body;
    const transaction = await Transaction.create({
      concept,
      amount,
      category,
      userId: req.userId
    });
    res.status(201).json(transaction);
  } catch (err) {
    res.status(500).json({ message: '服务器错误' });
  }
});

// 修改/删除交易同理,需先验证交易的userId与当前用户ID一致,防止越权操作

注意事项

  • JWT密钥不要硬编码,建议使用环境变量(如process.env.JWT_SECRET)
  • 密码加密的rounds设为10即可,过高会影响性能
  • 修改或删除交易时,需额外校验目标交易的userId是否等于当前用户ID,避免越权操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 20:54:17