270 lines
6.9 KiB
Go
270 lines
6.9 KiB
Go
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
|
||
}
|