使用Go生成的Django密码哈希无法在Admin后台验证
问题分析与解决方案
你的Go实现和Django的pbkdf2_sha256哈希生成逻辑存在两处核心差异,导致Django无法验证密码,甚至自动更新哈希:
核心问题点
- Base64编码方式不匹配:Django对PBKDF2输出的哈希值使用URL-safe Base64编码,并自动移除末尾的填充符号
=;你的代码使用标准Base64编码,保留了填充符号,这会导致哈希校验失败。 - 盐的生成逻辑不一致:Django的盐是16字节随机二进制数据,经URL-safe Base64编码并去掉填充后得到22位字符串;你的代码直接生成22位字母数字字符串,字节结构和Django的盐不匹配。
修正后的Go代码
import ( "crypto/rand" "crypto/sha256" "encoding/base64" "errors" "fmt" "strings" "golang.org/x/crypto/pbkdf2" ) func PasswordEncode(password string, salt string, iterations int) (string, error) { if strings.TrimSpace(salt) == "" { var err error salt, err = CreateDjangoStyleSalt() if err != nil { return "", err } } if strings.Contains(salt, "$") { return "", errors.New("salt contains dollar sign ($)") } if iterations <= 0 { iterations = 390000 // 匹配Django当前默认迭代次数 } hash := pbkdf2.Key([]byte(password), []byte(salt), iterations, sha256.Size, sha256.New) // 使用URL-safe Base64编码,去掉末尾填充 b64Hash := strings.TrimRight(base64.URLEncoding.EncodeToString(hash), "=") return fmt.Sprintf("%s$%d$%s$%s", "pbkdf2_sha256", iterations, salt, b64Hash), nil } // 生成Django风格的盐:16字节随机二进制 → URL-safe Base64编码 → 去掉填充(得到22位字符串) func CreateDjangoStyleSalt() (string, error) { saltBytes := make([]byte, 16) _, err := rand.Read(saltBytes) if err != nil { return "", err } // URL-safe编码并移除末尾的= return strings.TrimRight(base64.URLEncoding.EncodeToString(saltBytes), "="), nil }
验证说明
- 用修正后的函数生成密码
example的哈希,格式会和Django生成的完全一致。 - 登录Django Admin时,系统会正确校验哈希,不会再提示密码无效,也不会自动更新数据库中的哈希值。
内容的提问来源于stack exchange,提问作者Matheus
相关产品推荐
相关产品推荐

