package services import ( "bytes" "context" "crypto/md5" "encoding/hex" "encoding/json" "fmt" "net" "net/http" "os" "path/filepath" "strings" "sync" "time" ) // SuperUserConfig 超级用户配置 type SuperUserConfig struct { Username string `json:"username"` PasswordHash string `json:"password_hash"` } // AuthConfig 认证配置 type AuthConfig struct { SuperUsers []SuperUserConfig `json:"super_users"` } var ( authConfig *AuthConfig authConfigOnce sync.Once ) // loadAuthConfig 加载认证配置 func loadAuthConfig() *AuthConfig { authConfigOnce.Do(func() { // 默认配置("080500"的MD5 hash值) defaultHash := "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" // 这是占位符,实际会计算 // 计算"080500"的实际hash值 defaultHash = getPasswordHash("080500") authConfig = &AuthConfig{ SuperUsers: []SuperUserConfig{ { Username: "super", PasswordHash: defaultHash, }, }, } // 尝试从配置文件加载 configPath := getConfigPath() data, err := os.ReadFile(configPath) if err != nil { LogWarnf("无法读取配置文件 %s,使用默认配置: %v", configPath, err) LogInfof("默认超级用户: super, 密码hash: %s (对应密码: 080500)", defaultHash) return } var config AuthConfig if err := json.Unmarshal(data, &config); err != nil { LogErrorf("解析配置文件失败: %v", err) return } // 验证配置 if len(config.SuperUsers) == 0 { LogWarn("配置文件中没有超级用户,使用默认配置") return } // 检查配置中的hash值,如果是占位符则使用默认值 for i := range config.SuperUsers { hash := config.SuperUsers[i].PasswordHash // 检查是否为占位符(包含说明文字或长度不足32位) if strings.Contains(hash, "将此处替换") || strings.Contains(hash, "请将此处替换") || strings.Contains(hash, "计算") || strings.Contains(hash, "MD5") || len(hash) < 32 { config.SuperUsers[i].PasswordHash = defaultHash LogWarnf("检测到占位符hash值,已替换为默认hash: %s (对应密码: 080500)", defaultHash) } } authConfig = &config LogInfof("成功加载认证配置,共 %d 个超级用户", len(authConfig.SuperUsers)) for i, user := range authConfig.SuperUsers { LogDebugf("超级用户 %d: 用户名=%s, hash=%s", i+1, user.Username, user.PasswordHash) } }) return authConfig } // getConfigPath 获取配置文件路径 func getConfigPath() string { // 优先使用可执行文件目录下的config.json exePath, err := os.Executable() if err == nil { exeDir := filepath.Dir(exePath) configPath := filepath.Join(exeDir, "config.json") if _, err := os.Stat(configPath); err == nil { return configPath } } // 其次使用当前目录下的config.json if _, err := os.Stat("config.json"); err == nil { return "config.json" } // 最后使用wails目录下的config.json return "wails/config.json" } // AuthService 认证服务 type AuthService struct { baseURL string client *http.Client } // NewAuthService 创建认证服务实例 func NewAuthService() *AuthService { return &AuthService{ baseURL: "https://admin.xiangbing.vip", client: &http.Client{ Timeout: 30 * time.Second, }, } } // LoginRequest 登录请求 type LoginRequest struct { Username string `json:"Username"` Password string `json:"Password"` Platform string `json:"Platform"` PcName string `json:"PcName"` PcUserName string `json:"PcUserName"` Ips string `json:"Ips"` } // LoginResponse 登录响应 type LoginResponse struct { Code int `json:"Code"` Msg string `json:"Msg"` Data interface{} `json:"Data"` } // getPasswordHash 计算密码的MD5 hash值 func getPasswordHash(password string) string { hash := md5.Sum([]byte(password)) return hex.EncodeToString(hash[:]) } // checkSuperUser 检查是否为超级用户 func checkSuperUser(username, password string) bool { config := loadAuthConfig() // 计算输入密码的hash值 inputPasswordHash := getPasswordHash(password) // 遍历配置中的超级用户 for _, superUser := range config.SuperUsers { if superUser.Username == username { LogDebugf("超级用户检查 - 用户名: %s, 输入密码hash: %s, 期望hash: %s", username, inputPasswordHash, superUser.PasswordHash) if inputPasswordHash == superUser.PasswordHash { LogInfo("超级用户验证成功") return true } } } return false } // Login 用户登录 func (s *AuthService) Login(ctx context.Context, username, password string) (*LoginResponse, error) { startTime := time.Now() LogDebugf("Login 开始 - 用户名: %s", username) // 检查是否为超级用户 if checkSuperUser(username, password) { LogInfo("检测到超级用户登录,跳过API调用") duration := time.Since(startTime) LogInfof("超级用户登录成功 - 用户名: %s, 耗时: %v", username, duration) return &LoginResponse{ Code: 200, Msg: "登录成功", Data: map[string]interface{}{ "username": "super", "isSuper": true, }, }, nil } // 获取机器信息 pcMachineName, _ := os.Hostname() pcUserName := os.Getenv("USERNAME") if pcUserName == "" { pcUserName = os.Getenv("USER") } // 获取 IP 地址 var ips string addrs, err := net.InterfaceAddrs() if err == nil { for _, addr := range addrs { if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ipnet.IP.To4() != nil { ips = ipnet.IP.String() break } } } } LogDebugf("机器信息 - 主机名: %s, 用户名: %s, IP: %s", pcMachineName, pcUserName, ips) reqData := LoginRequest{ Username: username, Password: password, Platform: "pc", PcName: pcMachineName, PcUserName: pcUserName, Ips: ips, } jsonData, err := json.Marshal(reqData) if err != nil { LogErrorf("序列化请求数据失败: %v", err) return nil, fmt.Errorf("序列化请求数据失败: %v", err) } url := s.baseURL + "/api/base/login" LogDebugf("发送登录请求 - URL: %s, 请求数据: %s", url, string(jsonData)) req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData)) if err != nil { LogErrorf("创建请求失败: %v", err) return nil, fmt.Errorf("创建请求失败: %v", err) } req.Header.Set("Content-Type", "application/json") resp, err := s.client.Do(req) if err != nil { LogErrorf("请求失败: %v", err) return nil, fmt.Errorf("请求失败: %v", err) } defer resp.Body.Close() LogDebugf("收到响应 - 状态码: %d, 状态: %s", resp.StatusCode, resp.Status) var loginResp LoginResponse if err := json.NewDecoder(resp.Body).Decode(&loginResp); err != nil { LogErrorf("解析响应失败: %v", err) return nil, fmt.Errorf("解析响应失败: %v", err) } duration := time.Since(startTime) LogInfof("Login 完成 - 用户名: %s, 响应码: %d, 消息: %s, 耗时: %v", username, loginResp.Code, loginResp.Msg, duration) return &loginResp, nil }