VideoConcat/wails/services/auth_service.go
2026-01-07 17:59:30 +08:00

270 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}