Sequelize Setter 对更新没有影响

Sequelize Setter has no effect on update

我想使用模型中定义的 getter 和 setters 来加密和解密我数据库中的一些数据。但是在实现过程中我注意到,setters 在更新和创建时都会被触发,但只对创建有实际影响。因此创建一个条目调用 setter 并且(参见代码)“secret”的值在数据库中加密。但是如果我想更新这个条目,setter 被调用但没有任何更新。

如果我还更新没有为它们定义 setter 函数的值,则只会更新此值。

这是我的 setter:

secret: {
  type: DataTypes.STRING,
  defaultValue: null,
  async get() {
    try {
      return await crypt.decryptGetter(this, 'secret');
    } catch (err) { 
      throw new Error("Error while decrypting secret: " + err);
    }
  },
  async set(value) {
    try {
      return await crypt.encryptSetter(this, 'secret', value);
    } catch (err) {
      throw new Error("Error while encrypting secret: " + err);
    }
  }
}

我更经常使用这个 setters 和 getters,这就是我外包它们的原因。 如果需要,我会传递模型的当前实例以及键和值:

async decryptGetter(sequelizeModel, key) {
  try {
    return await module.exports.decrypt(sequelizeModel.getDataValue(key),config.internal.secret)
  } catch (err) {
    throw new Error("GETTER: " + err, __filename);
  }
},
    
async encryptSetter(sequelizeModel, key, value) {
  try {
    let encValue = await module.exports.encrypt(value, config.internal.secret);
    return sequelizeModel.setDataValue(key, encValue);
  } catch (err) {
    throw new Error("SETTER: " + err, __filename);
  }
}

有人可以帮我吗?一路上我错过了什么吗?提前致谢!

您尝试过使用 hooks 吗?

以下是我如何在创建和更新之前使用它们加密密码:

function hashPassword (user) {
    const SALT_FACTOR = 12

    if (!user.changed('password')) {
        return;
    } else {
        user.setDataValue('password', bcrypt.hashSync(user.password, SALT_FACTOR))
    }
}

module.exports = User = (sequelize, DataTypes) => {
    const User = sequelize.define('User', {
        id: {
            type: DataTypes.UUID,
            defaultValue: DataTypes.UUIDV4,
            primaryKey: true
        },
        password: {
            type: DataTypes.STRING
        },
        {
        hooks: {
            beforeCreate: hashPassword,
            beforeUpdate: hashPassword
        }
    })

    return User
}

正如 r9119 在他的回答中提到的,钩子是解决这个问题的正确方法! 我只是想我post总结一下我最后是怎么做的,以供以后遇到这个问题的其他人!

我在我的模型中添加了 beforeCreate 和 beforeUpdate 挂钩,并在 getter 方法中添加了一个 getter 函数:

const MyModel = sequelize.define(
  'MyModel', {
    MyModelID: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      allowNull: false,
      autoIncrement: true
    },
    name: {
      ...
    },
    secret: {
      ...
    }
  }, {
    timestamps: true,
    paranoid: true,
    freezeTableName: true,
    getterMethods: {
      async decrypt() {
        try {
          return await sequelizeUtils.cryptHookSpecific(this, config.internalSecret, modelCryptConfig.specificProps, projectDecrypt)
        } catch(err) {
          throw new Error(err);
        }
      }
    },
    hooks: {
      beforeCreate: async (myModel, options) => {
        try {
          await sequelizeUtils.cryptHookSpecific(myModel, config.internalSecret, modelCryptConfig.specificProps, projectDecrypt)
        } catch(err) {
          throw new Error(err);
        }
      },
      beforeUpdate: async (myModel, options) => {
        try {
          await sequelizeUtils.cryptHookSpecific(myModel, config.internalSecret, modelCryptConfig.specificProps, projectDecrypt)
        } catch(err) {
          throw new Error(err);
        }
      },
    }
  }
);

我为每个模型定义了一个 modelCryptConfig,它定义了哪些字段应该被解密。在那里我还可以为用于 de-/encryption:

的每个字段定义一个特殊键
let modelCryptConfig = {
    specificProps: [{field: "apiKey", key:"mySpecialKeyForThisField"},{field: "secret"},{field: "someInfo"},{field: "someOtherInfo"},{field: "somethingElse"},]
}

sequelizeUtils.cryptHookSpecific 函数接受模型、encrypt/decrypt 的密钥、应该加密/解密的字段以及最后一个实际执行 encryption/decryption 的项目特定函数.

async cryptHookSpecific(sequelizeModel, globalKey, specificProps, cryptFunction) {
  try {
    // loop over all specific properties to encrypt them
    for (let propIdx in specificProps) {
      if (!sequelizeModel.__proto__.rawAttributes[specificProps[propIdx].field].primaryKey && // is not a primary key
        !sequelizeModel.__proto__.rawAttributes[specificProps[propIdx].field].foreignKey && // is not a foreign key
        sequelizeModel.dataValues[specificProps[propIdx].field]) { // is not null
        // check if there is a specific key provided
        if (specificProps[propIdx].hasOwnProperty("key")) {
          // if so encrypt the property with the provided specific key
          sequelizeModel.dataValues[specificProps[propIdx].field] = await cryptFunction(sequelizeModel.dataValues[specificProps[propIdx].field], specificProps[propIdx].key)
        } else {
          // if not encrypt it with the globalKey
          sequelizeModel.dataValues[specificProps[propIdx].field] = await cryptFunction(sequelizeModel.dataValues[specificProps[propIdx].field], globalKey)
        }
      }
    }
  
    return sequelizeModel;
  } catch(err) {
    throw new Error(err);
  }
},

除了 cryptHookSpecific 函数之外,我还编写了一个 cryptHookComplete 函数,默认情况下 encrypts/decrypts 所有字段,您可以在 modelCryptConfig 中定义要排除的字段。

也许这可以帮助某人!再次感谢 r9119!