- 后端从 Python FastAPI 重写为 Go Gin(端口 56789) - 多班级完全隔离 - 超级管理员独立登录 - 课代表作业管理、排行榜分项排行 - 角色加减分上下限可配置 - 家长改密功能(可开关) - 周度/月度重置功能 - MySQL 5.7 兼容 - 43轮代码审查+全部修复 - Apache 2.0 许可证
111 lines
2.8 KiB
Go
111 lines
2.8 KiB
Go
// ===========================================
|
||
// 多班级版班级管理系统 - Go 后端
|
||
//
|
||
// 开发者: Canglan
|
||
// 联系方式: admin@sea-studio.top
|
||
// 版权归属: Sea Network Technology Studio
|
||
// 许可证: Apache License 2.0
|
||
//
|
||
// 版权所有 © Sea Network Technology Studio
|
||
// ===========================================
|
||
|
||
package crypto
|
||
|
||
import (
|
||
"crypto/md5"
|
||
"crypto/rand"
|
||
"crypto/sha1"
|
||
"crypto/subtle"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"math/big"
|
||
)
|
||
|
||
// HashPassword 密码哈希(与 Python 版完全兼容)
|
||
// 算法: MD5(SHA1(password) + salt)
|
||
// Python 参考: backend/utils/security.py -> sha1_md5_password()
|
||
// 已知弱算法:MD5 和 SHA1 均不适合密码哈希场景,保留此实现仅为兼容 Python 版数据。
|
||
// 后续迁移计划:迁移到 bcrypt/scrypt/argon2,并提供兼容层逐步过渡。
|
||
func HashPassword(password string, salt string) string {
|
||
// 第一层: SHA1(password)
|
||
sha1Hash := sha1.Sum([]byte(password))
|
||
sha1Hex := hex.EncodeToString(sha1Hash[:])
|
||
|
||
// 加盐: SHA1_hex + salt
|
||
salted := sha1Hex + salt
|
||
|
||
// 第二层: MD5(salted)
|
||
md5Hash := md5.Sum([]byte(salted))
|
||
return hex.EncodeToString(md5Hash[:])
|
||
}
|
||
|
||
// VerifyPassword 验证密码(使用常量时间比较,防止时序攻击)
|
||
func VerifyPassword(plainPassword, hashedPassword, salt string) bool {
|
||
computed := HashPassword(plainPassword, salt)
|
||
return subtle.ConstantTimeCompare([]byte(computed), []byte(hashedPassword)) == 1
|
||
}
|
||
|
||
// GenerateRandomPassword 生成随机密码
|
||
// 与 Python 版 SecurityUtils.generate_random_password() 兼容
|
||
func GenerateRandomPassword(length int) (string, error) {
|
||
alphabet := "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
||
result := make([]byte, length)
|
||
for i := range result {
|
||
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(alphabet))))
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成随机密码失败: %w", err)
|
||
}
|
||
result[i] = alphabet[n.Int64()]
|
||
}
|
||
return string(result), nil
|
||
}
|
||
|
||
// ValidatePasswordStrength 验证密码强度
|
||
// 要求: 大小写字母、数字、特殊符号至少包含 3 种,长度 6-20
|
||
func ValidatePasswordStrength(password string) (bool, string) {
|
||
if len(password) < 6 {
|
||
return false, "密码长度至少6位"
|
||
}
|
||
if len(password) > 20 {
|
||
return false, "密码长度不能超过20位"
|
||
}
|
||
|
||
hasUpper := false
|
||
hasLower := false
|
||
hasDigit := false
|
||
hasSpecial := false
|
||
|
||
for _, c := range password {
|
||
switch {
|
||
case c >= 'A' && c <= 'Z':
|
||
hasUpper = true
|
||
case c >= 'a' && c <= 'z':
|
||
hasLower = true
|
||
case c >= '0' && c <= '9':
|
||
hasDigit = true
|
||
default:
|
||
hasSpecial = true
|
||
}
|
||
}
|
||
|
||
charTypes := 0
|
||
if hasUpper {
|
||
charTypes++
|
||
}
|
||
if hasLower {
|
||
charTypes++
|
||
}
|
||
if hasDigit {
|
||
charTypes++
|
||
}
|
||
if hasSpecial {
|
||
charTypes++
|
||
}
|
||
|
||
if charTypes < 3 {
|
||
return false, "密码必须包含大写字母、小写字母、数字、特殊符号中的至少3种"
|
||
}
|
||
|
||
return true, ""
|
||
}
|