优化部分部署信息

This commit is contained in:
2026-08-01 19:59:55 +08:00
parent 2e58dc719a
commit 848062da84
2 changed files with 59 additions and 13 deletions
+2 -2
View File
@@ -115,7 +115,7 @@ func registerHandler(c *gin.Context) {
} }
if !ValidatePassword(req.Password) { if !ValidatePassword(req.Password) {
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "密码至少 8 位,需包含大小写字母数字和特殊字符"}) c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "密码至少 8 位,需包含大小写字母数字"})
return return
} }
@@ -205,7 +205,7 @@ func changePasswordHandler(c *gin.Context) {
} }
if !ValidatePassword(req.NewPassword) { if !ValidatePassword(req.NewPassword) {
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "密码至少 8 位,需包含大小写字母数字和特殊字符"}) c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "密码至少 8 位,需包含大小写字母数字"})
return return
} }
+57 -11
View File
@@ -13,6 +13,22 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
// ============================================================
// 密码策略常量(集中管理,便于后续调整)
// ============================================================
const (
// MinPasswordLength 密码最小长度
MinPasswordLength = 8
// PasswordRequirementMsg 统一错误提示(前端/后端共用)
PasswordRequirementMsg = "密码至少 8 位,需包含大小写字母和数字"
)
// ============================================================
// JWT 密钥管理
// ============================================================
var jwtSecretCache []byte var jwtSecretCache []byte
func getJwtSecret() []byte { func getJwtSecret() []byte {
@@ -27,11 +43,13 @@ func getJwtSecret() []byte {
return jwtSecretCache return jwtSecretCache
} }
// Claims JWT 载荷
type Claims struct { type Claims struct {
Username string `json:"username"` Username string `json:"username"`
jwt.RegisteredClaims jwt.RegisteredClaims
} }
// GenerateJWT 生成 JWT Token
func GenerateJWT(username string) (string, error) { func GenerateJWT(username string) (string, error) {
claims := Claims{ claims := Claims{
Username: username, Username: username,
@@ -44,6 +62,7 @@ func GenerateJWT(username string) (string, error) {
return token.SignedString(getJwtSecret()) return token.SignedString(getJwtSecret())
} }
// ParseJWT 解析 JWT Token
func ParseJWT(tokenString string) (*Claims, error) { func ParseJWT(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return getJwtSecret(), nil return getJwtSecret(), nil
@@ -57,25 +76,42 @@ func ParseJWT(tokenString string) (*Claims, error) {
return nil, errors.New("invalid token") return nil, errors.New("invalid token")
} }
// ============================================================
// 密码校验(核心策略)
// ============================================================
// ValidatePassword 校验密码强度
// 规则:长度 ≥ 8 位,且同时包含大写字母、小写字母、数字
// 注意:不强制要求特殊字符(与之前版本的区别)
func ValidatePassword(pwd string) bool { func ValidatePassword(pwd string) bool {
if len(pwd) < 8 { if len(pwd) < MinPasswordLength {
return false return false
} }
var hasUpper, hasLower, hasDigit, hasSpecial bool
var hasUpper, hasLower, hasDigit bool
for _, ch := range pwd { for _, ch := range pwd {
if ch >= 'A' && ch <= 'Z' { switch {
case ch >= 'A' && ch <= 'Z':
hasUpper = true hasUpper = true
} else if ch >= 'a' && ch <= 'z' { case ch >= 'a' && ch <= 'z':
hasLower = true hasLower = true
} else if ch >= '0' && ch <= '9' { case ch >= '0' && ch <= '9':
hasDigit = true hasDigit = true
} else if strings.ContainsAny(string(ch), "!@#$%^&*()_+-=[]{}|;:,.<>?") {
hasSpecial = true
} }
// 提前退出优化:一旦三类字符都已满足,立即返回 true
if hasUpper && hasLower && hasDigit {
return true
} }
return hasUpper && hasLower && hasDigit && hasSpecial
} }
return hasUpper && hasLower && hasDigit
}
// ============================================================
// 命令行管理员初始化(首次启动交互式创建)
// ============================================================
// InitAdminUser 交互式创建管理员账户(仅在无用户时触发)
func InitAdminUser() { func InitAdminUser() {
count, err := CountUsers() count, err := CountUsers()
if err != nil { if err != nil {
@@ -85,6 +121,7 @@ func InitAdminUser() {
log.Println("✅ 已存在管理员账户,跳过初始化") log.Println("✅ 已存在管理员账户,跳过初始化")
return return
} }
log.Println("⚠️ 首次启动,请设置管理员账户") log.Println("⚠️ 首次启动,请设置管理员账户")
log.Print("用户名: ") log.Print("用户名: ")
var username string var username string
@@ -92,10 +129,12 @@ func InitAdminUser() {
if username == "" { if username == "" {
username = "admin" username = "admin"
} }
for { for {
log.Print("密码 (至少8位,含大小写、数字、特殊字符): ") log.Printf("密码 (至少 %d 位,需包含大小写字母和数字): ", MinPasswordLength)
var password string var password string
fmt.Scanln(&password) fmt.Scanln(&password)
if ValidatePassword(password) { if ValidatePassword(password) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil { if err != nil {
@@ -110,12 +149,16 @@ func InitAdminUser() {
log.Println("✅ 管理员账户创建成功!") log.Println("✅ 管理员账户创建成功!")
break break
} else { } else {
log.Println("❌ 密码不符合复杂度要求,请重新输入") log.Printf("❌ 密码不符合要求: %s", PasswordRequirementMsg)
log.Println(" 要求: 至少8位,包含大小写字母、数字和特殊字符")
} }
} }
} }
// ============================================================
// Gin 中间件:JWT 认证
// ============================================================
// AuthMiddleware JWT 认证中间件
func AuthMiddleware() gin.HandlerFunc { func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization") authHeader := c.GetHeader("Authorization")
@@ -124,18 +167,21 @@ func AuthMiddleware() gin.HandlerFunc {
c.Abort() c.Abort()
return return
} }
parts := strings.SplitN(authHeader, " ", 2) parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" { if len(parts) != 2 || parts[0] != "Bearer" {
c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "认证令牌格式错误"}) c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "认证令牌格式错误"})
c.Abort() c.Abort()
return return
} }
claims, err := ParseJWT(parts[1]) claims, err := ParseJWT(parts[1])
if err != nil { if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "无效的认证令牌"}) c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "无效的认证令牌"})
c.Abort() c.Abort()
return return
} }
c.Set("username", claims.Username) c.Set("username", claims.Username)
c.Next() c.Next()
} }