2011-12-11 91 views
25

我剛剛爲我的新Grails項目添加了註冊功能。爲了測試它,我通過發送郵件和密碼進行了註冊。在將密碼保存到數據庫之前,我正在使用bcrypt算法對密碼進行散列處理。Bcrypt爲相同的輸入生成不同的哈希值?

但是,當我嘗試使用登錄時輸入的電子郵件和密碼進行登錄時,登錄失敗。我調試了應用程序,發現爲相同密碼生成的散列不同於我嘗試與已經散列的數據庫進行比較,因此登錄失敗(Registration.findByEmailAndPassword(params.email,hashPassd)in LoginController.groovy返回null)。

這裏是我的領域類Registration.groovy:

class Registration { 

    transient springSecurityService 

    String fullName 
    String password 
    String email 

    static constraints = { 
     fullName(blank:false) 
     password(blank:false, password:true) 
     email(blank:false, email:true, unique:true) 
    } 

    def beforeInsert = { 
     encodePassword() 
    } 

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

這裏是我的LoginController.groovy:

class LoginController { 

    /** 
    * Dependency injection for the springSecurityService. 
    */ 
    def springSecurityService 

    def index = { 
     if (springSecurityService.isLoggedIn()) { 
     render(view: "../homepage") 
     } 
     else { 
     render(view: "../index") 
     } 
    } 

    /** 
    * Show the login page. 
    */ 
    def handleLogin = { 

     if (springSecurityService.isLoggedIn()) { 
     render(view: "../homepage") 
     return 
     } 

     def hashPassd = springSecurityService.encodePassword(params.password) 
     // Find the username 
     def user = Registration.findByEmailAndPassword(params.email,hashPassd) 
     if (!user) { 
     flash.message = "User not found for email: ${params.email}" 
     render(view: "../index") 
     return 
     } else { 
     session.user = user 
     render(view: "../homepage") 
     } 
    } 
} 

下面是從我的Config.groovy告訴Grails的使用bcrypt算法的密碼散列片段和輪迴密鑰的數量:

grails.plugins.springsecurity.password.algorithm = 'bcrypt' 
grails.plugins.springsecurity.password.bcrypt.logrounds = 16 

回答

32

1月份是正確的 - 按設計加密不會爲每個輸入字符串生成相同的散列值。但是有一種方法可以檢查散列密碼是否有效,並且將其合併到關聯的密碼編碼器中。所以需要添加依賴注入在控制器(def passwordEncoder)的passwordEncoder豆和更改查找到

def handleLogin = { 

    if (springSecurityService.isLoggedIn()) { 
     render(view: "../homepage") 
     return 
    } 

    def user = Registration.findByEmail(params.email) 
    if (user && !passwordEncoder.isPasswordValid(user.password, params.password, null)) { 
     user = null 
    } 

    if (!user) { 
     flash.message = "User not found for email: ${params.email}" 
     render(view: "../index") 
     return 
    } 

    session.user = user 
    render(view: "../homepage") 
} 

請注意,你不編碼爲isPasswordValid通話密碼 - 傳遞明文密碼提交。

此外 - 完全不相關 - 將用戶存儲在會話中是一個壞主意。身份驗證委託人隨時可用,並存儲用戶ID,以便根據需要輕鬆地重新加載用戶(例如,User.get(springSecurityService.principal.id)。當您是服務器的唯一用戶時,存儲已斷開連接的潛在大型Hibernate對象在dev模式下工作良好,但可以大量的內存浪費,並迫使你解決被斷開的對象的工作(例如,必須使用merge等)。

+0

感謝Burt。它的工作,並非常感謝關於在會話中存儲用戶的建議。我是grails的新手,並且正在使用它來開發社交如果你能提出建議,我會很感激在最佳做法等或任何有幫助的....可能是一個鏈接到您的博客(一個solipsists軍隊.....我喜歡它) – adit

13

BCrypt散列包括salt,結果這個算法爲相同的輸入返回不同的哈希值。讓我用Ruby來展示它。

> require 'bcrypt' 
> p = BCrypt::Password.create "foobar" 
=> "$2a$10$DopJPvHidYqWVKq.Sdcy5eTF82MvG1btPO.81NUtb/4XjiZa7ctQS" 
> r = BCrypt::Password.create "foobar" 
=> "$2a$10$FTHN0Dechb/IiQuyeEwxaOCSdBss1KcC5fBKDKsj85adOYTLOPQf6" 
> p == "foobar" 
=> true 
> r == "foobar" 
=> true 

因此,BCrypt不能用於以您的示例中提供的方式查找用戶。應該使用替代的明確字段,例如,用戶名或電子郵件地址。

+0

如果我想在本地運行,那麼gem的名稱是什麼? –

+0

回答我自己的問題,寶石的名字是'bcrypt-ruby' –

+0

很好的回答,非常感謝 – gdgr