2011-12-11 41 views
25

Tôi vừa thêm chức năng đăng ký vào dự án Grails mới của mình. Để kiểm tra nó, tôi đã đăng ký bằng cách đưa ra một email và mật khẩu. Tôi đang sử dụng thuật toán bcrypt cho băm mật khẩu trước khi lưu nó vào cơ sở dữ liệu.Bcrypt tạo ra các băm khác nhau cho cùng một đầu vào?

Tuy nhiên khi tôi cố gắng đăng nhập bằng cùng một email và mật khẩu mà tôi đã cung cấp khi đăng ký, đăng nhập thất bại. Tôi đã gỡ lỗi ứng dụng và phát hiện ra rằng băm được tạo cho cùng một mật khẩu khác nhau khi tôi cố gắng so sánh với mật khẩu đã được băm từ cơ sở dữ liệu và do đó đăng nhập không thành công (Registration.findByEmailAndPassword (params.email, hashPassd) trong LoginController.groovy trả về null).

Đây là lớp tên miền của tôi 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) 
    } 
} 

Dưới đây là LoginController.groovy tôi:

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") 
     } 
    } 
} 

Dưới đây là một đoạn trích từ Config.groovy tôi nói grails sử dụng bcrypt thuật toán để băm mật khẩu và số vòng khóa:

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

Trả lời

32

Tháng một là đúng - bcrypt theo thiết kế không tạo ra cùng một giá trị băm cho mỗi chuỗi đầu vào. Nhưng có một cách để kiểm tra xem mật khẩu được băm có hợp lệ không và nó được tích hợp vào bộ mã hóa mật khẩu được liên kết. Vì vậy, thêm một dependency injection cho passwordEncoder đậu trong điều khiển của bạn (def passwordEncoder) và thay đổi tra cứu để

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") 
} 

Lưu ý rằng bạn không mã hóa mật khẩu cho isPasswordValid cuộc gọi - vượt qua trong các mật khẩu dạng cleartext nộp.

Ngoài ra - hoàn toàn không có liên quan - đó là ý tưởng tồi để lưu trữ người dùng trong phiên. Hiệu trưởng auth có sẵn và lưu trữ id người dùng để dễ dàng tải lại người dùng khi cần (ví dụ: User.get(springSecurityService.principal.id). Việc lưu trữ các đối tượng Hibernate có khả năng bị ngắt kết nối hoạt động tốt ở chế độ dev khi bạn là người dùng duy nhất của máy chủ của mình, nhưng có thể một sự lãng phí đáng kể của bộ nhớ và buộc bạn phải làm việc xung quanh các đối tượng bị ngắt kết nối (ví dụ như phải sử dụng merge, v.v.)

+0

Cảm ơn Burt. Nó thực sự cảm ơn đề xuất liên quan đến việc lưu trữ một người dùng trong phiên làm việc. Tôi mới sử dụng grails và đang sử dụng nó để phát triển một xã hội Tôi sẽ rất biết ơn nếu bạn có thể đưa ra đề xuất về các phương pháp hay nhất vv hoặc bất kỳ thứ gì có thể giúp .... có thể là một liên kết đến một bài đăng trên blog của bạn (một đội ngũ các nhà solipsists ..... tôi thích nó) – adit

13

Băm BCrypt bao gồm salt và kết quả là thuật toán này trả về các băm khác nhau cho cùng một đầu vào. Cho phép tôi thể hiện nó trong 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 

Do đó, không thể sử dụng BCrypt để tìm người dùng theo cách được trình bày trong ví dụ của bạn. Thay vào đó, nên sử dụng một trường rõ ràng thay thế, ví dụ: tên hoặc địa chỉ e-mail của người dùng.

+0

Nếu tôi muốn chạy cục bộ này, tên của đá quý là gì? –

+0

Trả lời câu hỏi của riêng tôi , tên đá quý là 'bcrypt-ruby' –

+0

Câu trả lời tuyệt vời, hãy đánh giá cao nó – gdgr

Các vấn đề liên quan