Spring Security Core and custom validator for a co

2019-05-24 23:32发布

问题:

If I write the following code:

static constraints = {      
    password blank: false, password: true , validator:{ val, obj -> 
        if (obj.password != obj.confirmarPassword)
            return 'usuario.password.dontmatch'
    }       
    confirmarPassword bindable: true
    }
}   

static transients = ['confirmarPassword']

The following error appears after introducing the same password in password and confirmarPassword:

null id in usuario.Usuario entry (don't flush the Session after an exception occurs)

I found out the root of the problem. It is the next comparison:

obj.password != obj.confirmarPassword

If I write the following code inside the validator:

println "password: ${obj.password}"
println "confirmarPassword: ${obj.confirmarPassword)}"

Eclipse prints:

password: testPassword
confirmarPassword: testPassword
password: fd5cb51bafd60f6fdbedde6e62c473da6f247db271633e15919bab78a02ee9eb
confirmarPassword: testPassword

As my second attempt I tried:

if (obj.password != obj.springSecurityService.encodePassword(obj.confirmarPassword))

The validation doesn't pass:

Property [password] of class [class usuario.Usuario] with value [testPassword] does not pass custom validation

And with:

println "password: ${obj.password}"
println "confirmarPassword: ${obj.springSecurityService.encodePassword(obj.confirmarPassword)}"

Eclipse prints:

password: testPassword
confirmarPassword: fd5cb51bafd60f6fdbedde6e62c473da6f247db271633e15919bab78a02ee9eb
password: fd5cb51bafd60f6fdbedde6e62c473da6f247db271633e15919bab78a02ee9eb
confirmarPassword: fd5cb51bafd60f6fdbedde6e62c473da6f247db271633e15919bab78a02ee9eb

For my third attempt I tried:

if (obj.springSecurityService.encodePassword(obj.password) != obj.springSecurityService.encodePassword(obj.confirmarPassword))

Again the same error:

null id in usuario.Usuario entry (don't flush the Session after an exception occurs)

The password looks encrypted twice the second time:

password: fd5cb51bafd60f6fdbedde6e62c473da6f247db271633e15919bab78a02ee9eb
confirmarPassword: fd5cb51bafd60f6fdbedde6e62c473da6f247db271633e15919bab78a02ee9eb
password: e8c3cb111bf39f848b9a20e186f1663fd5acb0ae018ddf5763ae65bd6f45151e
confirmarPassword: fd5cb51bafd60f6fdbedde6e62c473da6f247db271633e15919bab78a02ee9eb

I'm running out of ideas. What about the client side? Would it be secure to perform this validation with Javascript?

This is my full domain class code:

package usuario

class Usuario implements Serializable {

    transient springSecurityService

    String username
    String password
    boolean enabled
    boolean accountExpired
    boolean accountLocked
    boolean passwordExpired

    String confirmarPassword
    String nombre
    String apellidos
    String email
    String telefono
    String documentoIdentificacion
    boolean aceptarPoliticaDeProteccionDeDatos = Boolean.FALSE

    static transients = ['confirmarPassword', 'aceptarPoliticaDeProteccionDeDatos']

    static constraints = {
        username blank: false, unique: true
        password blank: false, password: true 
        confirmarPassword bindable: true, blank:false, password: true, validator:{ val, obj -> 
            println "password: ${obj.password}"
            println "confirmarPassword: ${val}"     

            if ((obj.password != val))
                return 'usuario.password.dontmatch' 
        }

        nombre blank: false
        apellidos blank: false
        email blank: false, email:true
        telefono blank: false, matches: "[0-9 -+()]{3,15}"
        documentoIdentificacion blank: false
        aceptarPoliticaDeProteccionDeDatos bindable: true, validator:{ val, obj ->
            if (!obj.id && obj.aceptarPoliticaDeProteccionDeDatos != true) //> !obj.id: si el usuario ya no está creado. Si ya está creado es que lo está modificando y no es necesario que vuelva a aceptar la política.
                return 'usuario.politicaprotecciondatos.notaccepted'
        }
    }

    static mapping = {
        password column: '`password`'
    }

    Set<Rol> getAuthorities() {

            UsuarioRol.findAllByUsuario(this).collect { it.rol } as Set

    }

    def beforeInsert() {
        encodePassword()
    }

    def beforeUpdate() {
        if (isDirty('password')) {
            encodePassword()
        }
    }

    protected void encodePassword() {
        password = springSecurityService.encodePassword(password)
    }
}

回答1:

If you are using spring security, you can try this:

class User{
String password

static mapping = {
    password column: '`password`'
}

Set<Role> getAuthorities() {
    UserRole.findAllByUser(this).collect { it.role } as Set
}

def beforeInsert() {
    encodePassword()
}

def beforeUpdate() {
    if (isDirty('password')) {
        encodePassword()
    }
}

protected void encodePassword() {
    password = springSecurityService.encodePassword(password)
 }
}

It will hep you to save encoded password in database.

You can make a command object take a variable password and encode password like this:

@Validateable
class CommandObject implements Serializable {

String password
String confirmPassword

static constraints = {

    password nullable: false, blank: false
    confirmPassword nullable: false, blank: false, validator: { val, object ->
        if ((val != object.password)) {
            return 'passwordMismatch'
        }
        return true
    }        
 }
}


回答2:

Grails Spring Security Plugin encrypts your password before insert, but there is no need to invoke it unless you are actually comparing the current password to an old password. So a change password command object can have the following constraints:

static constraints = { 
    oldPassword validator: {val, obj ->
       def actual = obj.springSecurityService.encodePassword(val)
       if(actual != obj.secUser.password) 'old.password.must.match.current.password'
    }
    newPassword validator: {val, obj ->
       if(val == obj.oldPassword) 'new.password.must.differ.from.old'
    }
    confirmPassword validator: {val, obj ->
       if(val != obj.newPassword) 'confirm.password.must.match.new.password'
    }
}