Golang中Bcrypt密码不匹配问题如何解决

Golang中Bcrypt密码不匹配问题如何解决 当我从MySQL数据库获取哈希密码并与请求负载进行比较时,函数bcrypt.CompareHashAndPassword返回false。以下是代码,有人能帮助我吗?

哈希函数: 包 hasher

import (
	"golang.org/x/crypto/bcrypt"
)

// HashPassword 函数
func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	return string(bytes), err
}

// CheckPasswordHash 函数
func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

创建会话的方法: 包 session

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/kielkow/Post-Service/shared/http/cors"
	"github.com/kielkow/Post-Service/shared/providers/apperror"
	"github.com/kielkow/Post-Service/shared/providers/authentication"
	"github.com/kielkow/Post-Service/shared/providers/hasher"
)

const sessionBasePath = "session"

// SetupRoutes 函数
func SetupRoutes(apiBasePath string) {
	handleSession := http.HandlerFunc(postsHandler)

	http.Handle(fmt.Sprintf("%s/%s", apiBasePath, sessionBasePath), cors.Middleware(handleSession))
}

func postsHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodPost:
		var session Session
		bodyBytes, err := ioutil.ReadAll(r.Body)

		if err != nil {
			error := apperror.GenerateError(400, err.Error())

			w.WriteHeader(http.StatusBadRequest)
			w.Write(error)
			return
		}

		err = json.Unmarshal(bodyBytes, &session)

		if err != nil {
			error := apperror.GenerateError(400, err.Error())

			w.WriteHeader(http.StatusBadRequest)
			w.Write(error)
			return
		}

		passwordHashed, err := getPassword(session.Email)

		if err != nil {
			error := apperror.GenerateError(403, err.Error())

			w.WriteHeader(http.StatusNonAuthoritativeInfo)
			w.Write(error)
			return
		}

		match := hasher.CheckPasswordHash(session.Password, passwordHashed)

		if match == false {
			error := apperror.GenerateError(403, "Incorret credentials")

			w.WriteHeader(http.StatusNonAuthoritativeInfo)
			w.Write(error)
			return
		}

		token, err := authentication.GenerateJWT(session.Email)

		tokenJSON, err := json.Marshal(token)

		w.Header().Set("Content-Type", "application/json")
		w.Write(tokenJSON)
		return

	case http.MethodOptions:
		return
	}
}

更多关于Golang中Bcrypt密码不匹配问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

@kielkow 你最终解决这个问题了吗?

更多关于Golang中Bcrypt密码不匹配问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题通常出现在密码哈希的存储或比较环节。以下是可能导致bcrypt密码不匹配的常见原因及解决方案:

1. 数据库字段长度问题

确保MySQL中存储哈希密码的字段足够长,bcrypt哈希值通常是60个字符:

ALTER TABLE users MODIFY password_hash VARCHAR(255);

2. 哈希值存储问题

检查从数据库获取密码哈希的getPassword函数:

func getPassword(email string) (string, error) {
    var passwordHash string
    err := db.QueryRow("SELECT password_hash FROM users WHERE email = ?", email).Scan(&passwordHash)
    if err != nil {
        return "", err
    }
    
    // 调试输出,查看实际获取的哈希值
    fmt.Printf("从数据库获取的哈希值: %s\n", passwordHash)
    fmt.Printf("哈希值长度: %d\n", len(passwordHash))
    
    return passwordHash, nil
}

3. 密码哈希创建时的检查

在创建用户时验证哈希过程:

func createUserHandler(w http.ResponseWriter, r *http.Request) {
    // ... 解析请求
    
    // 生成哈希
    hashedPassword, err := hasher.HashPassword(user.Password)
    if err != nil {
        // 处理错误
    }
    
    // 调试输出
    fmt.Printf("生成的哈希值: %s\n", hashedPassword)
    fmt.Printf("哈希值长度: %d\n", len(hashedPassword))
    
    // 立即验证
    match := hasher.CheckPasswordHash(user.Password, hashedPassword)
    fmt.Printf("立即验证结果: %v\n", match)
    
    // 存储到数据库
    // ...
}

4. 直接调试比较函数

添加详细的调试信息到会话处理器:

func postsHandler(w http.ResponseWriter, r *http.Request) {
    // ... 之前的代码
    
    passwordHashed, err := getPassword(session.Email)
    if err != nil {
        // 处理错误
    }
    
    // 调试信息
    fmt.Printf("请求密码: %s\n", session.Password)
    fmt.Printf("数据库哈希: %s\n", passwordHashed)
    fmt.Printf("哈希长度: %d\n", len(passwordHashed))
    
    // 直接使用bcrypt比较进行调试
    err = bcrypt.CompareHashAndPassword([]byte(passwordHashed), []byte(session.Password))
    if err != nil {
        fmt.Printf("bcrypt比较错误: %v\n", err)
        // 返回错误
    }
    
    // ... 其余代码
}

5. 检查数据库编码问题

确保数据库连接使用正确的字符集:

// 数据库连接字符串添加字符集参数
dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := sql.Open("mysql", dsn)

6. 验证哈希格式

添加一个验证函数检查哈希格式:

func isValidBcryptHash(hash string) bool {
    // bcrypt哈希以 $2a$, $2b$, $2x$ 或 $2y$ 开头
    if len(hash) < 60 {
        return false
    }
    
    prefix := hash[:4]
    validPrefixes := []string{"$2a$", "$2b$", "$2x$", "$2y$"}
    
    for _, p := range validPrefixes {
        if strings.HasPrefix(hash, p) {
            return true
        }
    }
    return false
}

// 在比较前使用
if !isValidBcryptHash(passwordHashed) {
    fmt.Println("无效的bcrypt哈希格式")
    // 处理错误
}

7. 完整示例修复

如果问题仍然存在,尝试这个最小化的测试用例:

package main

import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
)

func main() {
    password := "myPassword123"
    
    // 生成哈希
    hash, err := bcrypt.GenerateFromPassword([]byte(password), 14)
    if err != nil {
        panic(err)
    }
    
    hashStr := string(hash)
    fmt.Printf("生成的哈希: %s\n", hashStr)
    
    // 模拟从数据库获取(确保没有额外空格或换行)
    dbHash := hashStr // 在实际中从数据库获取
    
    // 比较
    err = bcrypt.CompareHashAndPassword([]byte(dbHash), []byte(password))
    if err != nil {
        fmt.Printf("比较失败: %v\n", err)
    } else {
        fmt.Println("密码匹配成功")
    }
}

运行这个测试来确定你的bcrypt实现是否正确工作。如果测试通过但你的代码不工作,问题可能出在数据库存储或检索过程中。

回到顶部