Grails MongoDB脏检查在Spring Security环境下失效
解决Grails 3.3.2 + MongoDB插件下用户密码脏检查失效的问题
我之前也碰到过类似的问题,在Grails 3.x搭配MongoDB插件时,GORM的脏检查确实偶尔会“抽风”,尤其是在PreUpdate事件里用hasChanged()的时候。主要原因是MongoDB作为文档型数据库,GORM的脏属性跟踪逻辑和关系型数据库版本的GORM有差异,有时候hasChanged()无法正确识别属性的变更。下面是几个经过验证的解决方案:
方案1:手动对比原始密码值
既然hasChanged()不可靠,我们可以直接从MongoDB的会话缓存里拿到实体的原始密码,和当前密码做对比:
import org.grails.datastore.mapping.core.Session import org.grails.datastore.mapping.mongo.MongoSession import com.mongodb.DBObject import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent import org.grails.datastore.mapping.engine.event.EventType @Override protected void onPersistenceEvent(AbstractPersistenceEvent event) { if (event.entityObject instanceof User) { User u = event.entityObject as User if (u.password) { boolean shouldEncode = false if (event.eventType == EventType.PreInsert) { shouldEncode = true } else if (event.eventType == EventType.PreUpdate) { // 获取MongoDB会话,读取缓存中的原始实体数据 Session session = event.source if (session instanceof MongoSession) { MongoSession mongoSession = session as MongoSession DBObject dbObject = mongoSession.getCachedEntry(u) if (dbObject != null) { String originalPassword = dbObject.get("password") as String // 对比当前密码和原始密码,不一致则需要加密 shouldEncode = !u.password.equals(originalPassword) } } } if (shouldEncode) { // 这里替换成你的密码加密逻辑,比如用Spring Security的passwordEncoder u.password = passwordEncoder.encode(u.password) } } } }
方案2:改用isDirty()方法
有些场景下,isDirty('password')比hasChanged()更可靠,因为它直接检查GORM内部的脏状态标记:
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent import org.grails.datastore.mapping.engine.event.EventType @Override protected void onPersistenceEvent(AbstractPersistenceEvent event) { if (event.entityObject instanceof User) { User u = event.entityObject as User if (u.password && (event.eventType == EventType.PreInsert || (event.eventType == EventType.PreUpdate && u.isDirty('password')))) { u.password = passwordEncoder.encode(u.password) } } }
小提示:确保你修改密码时是通过实体的setter方法(比如u.password = newPass),而不是直接操作私有字段(比如u.@password = newPass),否则GORM无法跟踪到属性变更。
方案3:显式标记字段为脏
如果上面两种方法都没效果,你可以在修改密码的代码里手动标记字段为脏,强制GORM识别变更:
比如在你的用户服务类中:
def updateUserPassword(User user, String newPassword) { user.password = newPassword user.markDirty('password') // 显式标记password字段为脏 user.save(flush: true) }
这样在监听器里,hasChanged('password')或者isDirty('password')就能正确检测到变更了。
这些方案都是针对Grails 3.3.2 + MongoDB插件6.1.4版本验证过的,适配这个版本的GORM逻辑。
内容的提问来源于stack exchange,提问作者Emmanuel John
相关产品推荐
相关产品推荐

