重构进行中,preview通道尚未完全恢复全部功能

This commit is contained in:
2026-08-07 19:16:08 +08:00
parent 5bce105bd6
commit 3a381c85ed
21 changed files with 889 additions and 2416 deletions
+234
View File
@@ -0,0 +1,234 @@
package auth
import (
"errors"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
"frpc-console/internal/db"
)
var jwtSecretCache []byte
// getJwtSecret 从数据库获取 JWT 密钥
func getJwtSecret() []byte {
if len(jwtSecretCache) > 0 {
return jwtSecretCache
}
secret, err := db.GetJwtSecret()
if err != nil {
// 如果数据库还没有密钥,生成一个默认的(仅用于开发)
// 生产环境应该通过数据库初始化时生成
return []byte("frpc-console-default-secret-key-2024")
}
jwtSecretCache = []byte(secret)
return jwtSecretCache
}
// Claims JWT 声明
type Claims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
// GenerateJWT 生成 JWT
func GenerateJWT(username string) (string, error) {
claims := Claims{
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(getJwtSecret())
}
// ParseJWT 解析 JWT
func ParseJWT(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return getJwtSecret(), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
// ValidatePassword 校验密码复杂度
// 要求: 至少8位,包含大小写字母、数字、特殊字符
func ValidatePassword(pwd string) bool {
if len(pwd) < 8 {
return false
}
var hasUpper, hasLower, hasDigit, hasSpecial bool
for _, ch := range pwd {
switch {
case ch >= 'A' && ch <= 'Z':
hasUpper = true
case ch >= 'a' && ch <= 'z':
hasLower = true
case ch >= '0' && ch <= '9':
hasDigit = true
case strings.ContainsAny(string(ch), "!@#$%^&*()_+-=[]{}|;:,.<>?"):
hasSpecial = true
}
}
return hasUpper && hasLower && hasDigit && hasSpecial
}
// HashPassword 加密密码
func HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hash), nil
}
// CheckPassword 验证密码
func CheckPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// ================================================================
// 登录 / 注册 / 修改密码 的业务逻辑 (供 handler 调用)
// ================================================================
// LoginRequest 登录请求
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
// LoginResponse 登录响应
type LoginResponse struct {
Token string `json:"token"`
}
// Login 登录业务逻辑
func Login(username, password string) (string, error) {
user, err := db.GetUserByUsername(username)
if err != nil {
return "", errors.New("用户不存在")
}
if !CheckPassword(password, user.PasswordHash) {
return "", errors.New("密码错误")
}
return GenerateJWT(username)
}
// RegisterRequest 注册请求
type RegisterRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
// Register 注册业务逻辑
func Register(username, password string) (string, error) {
// 检查用户名是否已存在
exists, err := db.UserExists(username)
if err != nil {
return "", err
}
if exists {
return "", errors.New("用户名已存在")
}
// 密码复杂度校验
if !ValidatePassword(password) {
return "", errors.New("密码不符合复杂度要求")
}
// 加密密码
hash, err := HashPassword(password)
if err != nil {
return "", err
}
// 创建用户
if err := db.CreateUser(username, hash); err != nil {
return "", err
}
// 生成 JWT
return GenerateJWT(username)
}
// ChangePasswordRequest 修改密码请求
type ChangePasswordRequest struct {
OldPassword string `json:"oldPassword"`
NewPassword string `json:"newPassword"`
}
// ChangePassword 修改密码业务逻辑
func ChangePassword(username, oldPassword, newPassword string) error {
user, err := db.GetUserByUsername(username)
if err != nil {
return errors.New("用户不存在")
}
if !CheckPassword(oldPassword, user.PasswordHash) {
return errors.New("原密码错误")
}
if !ValidatePassword(newPassword) {
return errors.New("新密码不符合复杂度要求")
}
hash, err := HashPassword(newPassword)
if err != nil {
return err
}
return db.UpdateUserPassword(username, hash)
}
// HasUsers 检查是否存在用户
func HasUsers() (bool, error) {
count, err := db.CountUsers()
if err != nil {
return false, err
}
return count > 0, nil
}
// ================================================================
// Gin 中间件
// ================================================================
// AuthMiddleware JWT 认证中间件
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "未提供认证令牌"})
c.Abort()
return
}
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "认证令牌格式错误"})
c.Abort()
return
}
claims, err := ParseJWT(parts[1])
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "无效的认证令牌"})
c.Abort()
return
}
c.Set("username", claims.Username)
c.Next()
}
}
// GetUsernameFromContext 从 gin.Context 获取当前用户名
func GetUsernameFromContext(c *gin.Context) string {
username, exists := c.Get("username")
if !exists {
return ""
}
return username.(string)
}