Spring Security 如何将使用SHA-2.5.6散列密码的帐户从1Grails 2.5.6迭代迁移到Grails 4.0.12

e3bfsja2  于 2022-12-29  发布在  Spring
关注(0)|答案(1)|浏览(152)

我最近开始尝试将这个应用程序从Grails 256更新到Grails 4.0.12。我对Grails和编程都很陌生,所以已经3周了,几百次尝试都没有成功。除了密码散列之外,一切似乎都很好。由于密码是使用SHA 256散列的,用户无法登录。我“我尝试了各种互联网上的实现,但遗憾的是没有一个能成功,所以stackoverflow是我最后的尝试。然而,仅仅是为了上下文,在任何人想知道的情况下都不会抛出错误;但同时,如果你要求任何例外,你可能不知道如何解决它:(.嗯,感谢任何人有任何提示。
我试过创建一个Sha 256到BCrypt编码器,如Burt Beckwith here所述,但没有成功。这种方法似乎不适合我的情况。

soat7uwm

soat7uwm1#

对于那些可能遇到这个问题的人,我写了一个非常有趣的方法。
Grails 4提供了一个特性,允许自定义编码器(codec)实现,只要类位于utils文件夹下,并且类名应该以Codec结尾,即class MyEncoderCodec。阅读更多关于here的信息。因此,对于使用SHA-256(迭代一次)的用户,解决方案如下:
1.创建编码器

//path grails-app/Utils/OldEncoderCodec.groovy

import java.security.MessageDigest

class OldEncoderCodec {
    static encode = { String rawPass ->
        MessageDigest md = MessageDigest.getInstance("SHA-256") 
        md.update(rawPass.getBytes())
        byte[] byteData = md.digest()
        //convert the byte to hex format - test1
        StringBuffer sb = new StringBuffer()
        for (int i = 0; i < byteData.length; i++) {
            sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1))
        }
        return sb.toString()
    }
}

1.创建一个控制器方法来处理转换。

import app.Appuser
import grails.converters.JSON
import grails.gorm.transactions.Transactional
import grails.plugin.springsecurity.annotation.Secured

class HelpController {

    def springSecurityService

    @Transactional
    def ajaxEncodeAndUpdatePasswords(){
        def username = request.getJSON().u
        def rawPass = request.getJSON().p
        boolean result = true //I am always returning true, so handle this as you need to; i.e. you could return false if the user was not found and display a message on the form instead of submitting.

        def u = Appuser.findByUsername(username)
        if(u){
            def dbPass = u.password
            if(!dbPass.startsWith("{")) {
                def encodedPass = rawPass.encodeAsOldEncoder()
                if(dbPass == encodedPass){
                    u.password = rawPass
                    u.save(flush: true)
                }
            }
        }
        render ([result:result] as JSON)
    }
}

1.使用 AJAX 在表单提交前进行转换;你可以使用拦截器,但我认为 AJAX 调用更干净,你可以避免重写和依赖注入。我是编程新手,所以我知道这个javascript代码可以改进。还要注意,在我的例子中,#login-button是提交表单的提交按钮;处理回车键。

//path views/login/auth.gsp

$(document).ready(function(){
    $('#login-button').on('click', function(event){
        event.preventDefault();
        nCode()
    });
    $(window).keydown(function(event){
        if(event.keyCode === 13) {
            event.preventDefault();
            nCode()
        }
    });

    function nCode(){
        const u=$('#username').val()
        const p=$('#password').val()
        $.ajax({
            url:'${createLink(controller: 'help', action: 'ajaxEncodeAndUpdatePasswords')}',
            contentType:'json',
            method:'POST',
            data: JSON.stringify({
                u:u,
                p:p
            })
        }).done((data)=>{
            $('#loginForm').submit()
        })
    }

1.老实说,我不是100%肯定,我现在懒得检查,但我几乎可以肯定,在Grails 4中,当你创建一个项目时,Grails会自动创建一个PasswordEncoderListener。

//path src/main/groovy/AppuserPasswordEncoderListener.groovy

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 AppuserPasswordEncoderListener {

    @Autowired
    SpringSecurityService springSecurityService

    @Listener(Appuser)
    void onPreInsertEvent(PreInsertEvent event) {
        encodePasswordForEvent(event)
    }

    @Listener(Appuser)
    void onPreUpdateEvent(PreUpdateEvent event) {
        encodePasswordForEvent(event)
    }

    private void encodePasswordForEvent(AbstractPersistenceEvent event) {
        if (event.entityObject instanceof Appuser) {
            Appuser u = event.entityObject as Appuser
            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
    }
}

如您所见,我没有使用编码器来验证用户,只是检查匹配,然后我只是更新数据库中的用户密码,并让spring security处理编码。

相关问题