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!
我想使用模型中定义的 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!