2011-12-11 94 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

相关问题