feat(auth): JWT(HS256 stdlib) + RBAC(owner/admin/viewer) + API key + 密码 hash;config 校验
This commit is contained in:
1 parent
7f7cb8fbf7
commit
a3f6f4f687
10 files changed
+1116
No files matched your search
@@ -0,0 +1,68 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HashPassword derives a salted, iterated SHA-256 digest of password and
|
||||
// returns it encoded as "sha256$<salt>$<digest>". Each call uses a fresh random
|
||||
// salt, so equal passwords produce different encodings.
|
||||
//
|
||||
// NOTE: this is a self-contained, dependency-free scheme that is deliberately
|
||||
// weaker than bcrypt (cost 12). It is acceptable for the current single-binary
|
||||
// milestone; swap to golang.org/x/crypto/bcrypt before exposing auth to
|
||||
// untrusted parties in production.
|
||||
func HashPassword(password string) (string, error) {
|
||||
salt := make([]byte, saltLength)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", fmt.Errorf("auth: generate salt: %w", err)
|
||||
}
|
||||
digest := derivePassword(password, salt, passwordIterations)
|
||||
return passwordScheme + "$" +
|
||||
base64.RawStdEncoding.EncodeToString(salt) + "$" +
|
||||
base64.RawStdEncoding.EncodeToString(digest), nil
|
||||
}
|
||||
|
||||
// CheckPassword reports whether password matches the encoded hash. Malformed
|
||||
// input, an unknown scheme, or a mismatch all return false.
|
||||
func CheckPassword(password, encoded string) bool {
|
||||
scheme, salt, digest, err := parsePasswordHash(encoded)
|
||||
if err != nil || scheme != passwordScheme {
|
||||
return false
|
||||
}
|
||||
want := derivePassword(password, salt, passwordIterations)
|
||||
return subtle.ConstantTimeCompare(want, digest) == 1
|
||||
}
|
||||
|
||||
// parsePasswordHash splits an encoded hash into scheme, salt, and digest.
|
||||
func parsePasswordHash(encoded string) (scheme string, salt, digest []byte, err error) {
|
||||
parts := strings.Split(encoded, "$")
|
||||
if len(parts) != 3 {
|
||||
return "", nil, nil, fmt.Errorf("auth: malformed password hash")
|
||||
}
|
||||
salt, err = base64.RawStdEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return "", nil, nil, fmt.Errorf("auth: decode salt: %w", err)
|
||||
}
|
||||
digest, err = base64.RawStdEncoding.DecodeString(parts[2])
|
||||
if err != nil {
|
||||
return "", nil, nil, fmt.Errorf("auth: decode digest: %w", err)
|
||||
}
|
||||
return parts[0], salt, digest, nil
|
||||
}
|
||||
|
||||
// derivePassword returns the iterated salted SHA-256 digest of password.
|
||||
func derivePassword(password string, salt []byte, iterations int) []byte {
|
||||
sum := sha256.Sum256(append(salt, []byte(password)...))
|
||||
out := sum[:]
|
||||
for i := 1; i < iterations; i++ {
|
||||
cur := sha256.Sum256(append(out, salt...))
|
||||
out = cur[:]
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in new issue
Block a user