Грязные проверки Grails MongoDB с помощью Spring Security
Я использую Grails 3.3.2 с плагином mongoDB (v6.1.4) и плагином Spring Security Core (v3.2.0).
У меня есть следующее UserPasswordEncoderListener
с помощью следующего метода persistenceEvent:
@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.hasChanged('password')))) {
event.getEntityAccess().setProperty("password", encodePassword(u.password))
}
}
}
Проблема заключается в hasChanged
call всегда возвращает true каждый раз, когда я сохраняю объект пользователя, в котором НЕТ обновлений, что приводит к перекодированию уже закодированного пароля и, таким образом, к нарушению аутентификации.
Одним из обходных путей будет сделать это по-старому и просто получить исходный пароль из БД и сравнить его перед кодированием, но мне интересно, почему hasChanged
ложно возвращает истину.
Я проверял это hasChanged
ведет себя правильно в другом месте, запустив в консоли Groovy следующее:
def user = User.findByEmail("user@email.com")
println "Result: "+ user.hasChanged('password')
Результат Result: false
, Почему это НЕ работает в классе слушателей постоянства?
К вашему сведению: у меня определен следующий bean-компонент в resources.groovy:
userPasswordEncoderListener(UserPasswordEncoderListener,ref('mongoDatastore'))
2 ответа
Вы пытались использовать isDirty()
скорее, чем hasChanged()
в твоем слушателе?
Например:
package com.mycompany.myapp
import grails.plugin.springsecurity.SpringSecurityService
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent
import org.grails.datastore.mapping.engine.event.PreInsertEvent
import org.grails.datastore.mapping.engine.event.PreUpdateEvent
import org.springframework.beans.factory.annotation.Autowired
import grails.events.annotation.gorm.Listener
import groovy.transform.CompileStatic
@CompileStatic
class UserPasswordEncoderListener {
@Autowired
SpringSecurityService springSecurityService
@Listener(User)
void onPreInsertEvent(PreInsertEvent event) {
encodePasswordForEvent(event)
}
@Listener(User)
void onPreUpdateEvent(PreUpdateEvent event) {
encodePasswordForEvent(event)
}
private void encodePasswordForEvent(AbstractPersistenceEvent event) {
if (event.entityObject instanceof User) {
User u = event.entityObject as User
if (u.password && ((event instanceof PreInsertEvent) || (event instanceof PreUpdateEvent && u.isDirty('password')))) {
event.getEntityAccess().setProperty('password', encodePassword(u.password))
}
}
}
private String encodePassword(String password) {
springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
}
Дополнительная информация доступна по адресу: https://grails-plugins.github.io/grails-spring-security-core/3.2.x/index.html
Временное решение на данный момент:
@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.hasChanged('password')))) {
if(event.eventType == EventType.PreUpdate){ //Temp workaround until hasChanged behaves correctly
def originalUser = User.get(u?.id)
if(originalUser.password != u.password){
event.getEntityAccess().setProperty("password", encodePassword(u.password))
}
}else {
event.getEntityAccess().setProperty("password", encodePassword(u.password))
}
}
}
}
Оставил проблему в репозитории github ( https://github.com/grails-plugins/grails-spring-security-core/issues/539). Буду обновлять с обратной связью.