版本封存

This commit is contained in:
zeaslity
2025-12-06 11:26:05 +08:00
parent 13949e1ba8
commit c0ae5e30c4
57 changed files with 2443 additions and 1428 deletions

View File

@@ -2,14 +2,15 @@ package services
import (
models2 "cmii-uav-watchdog-common/models"
"cmii-uav-watchdog-common/totp_tier_one"
"cmii-uav-watchdog-common/totp_tier_two"
"cmii-uav-watchdog-common/utils"
"cmii-uav-watchdog-common/wdd_log"
"cmii-uav-watchdog/config"
"cmii-uav-watchdog/utils"
"encoding/json"
"errors"
"log"
"os"
"sync"
"time"
)
// 单例相关变量
@@ -21,11 +22,11 @@ var (
// AuthService 授权服务
type AuthService struct {
mu sync.RWMutex
hostInfoSet map[string]models2.HostInfo // 主机信息集合
authorizationInfo models2.AuthorizationStorage // 授权信息
totpService *TOTPService
initialized bool
mu sync.RWMutex
heartBeatHostInfoMap map[string]models2.HostInfo // 主机信息集合
authorizationInfo *models2.AuthorizationStorage // 授权信息
totpService *TOTPService
initialized bool
}
// NewAuthService 创建授权服务(单例模式)
@@ -42,20 +43,25 @@ func NewAuthService() *AuthService {
// 创建新实例
service := &AuthService{
hostInfoSet: make(map[string]models2.HostInfo),
totpService: NewTOTPService(),
initialized: false,
heartBeatHostInfoMap: make(map[string]models2.HostInfo),
totpService: NewTOTPService(),
initialized: false,
}
// 尝试从本地加载授权信息
service.loadAuthorizationInfo()
// 确保 authorizationInfo 不为 nil
if service.authorizationInfo == nil {
service.authorizationInfo = &models2.AuthorizationStorage{}
}
// 判断 项目级别的 TOTP密钥是否为空
// 若为空 则生成一个 二级TOTP密钥 然后持久化写入到授权文件中
if service.authorizationInfo.SecondTOTPSecret == "" {
secondTOTPSecret, err := service.totpService.GenerateTierTwoTOTPSecret()
secondTOTPSecret, err := totp_tier_two.GenerateTierTwoTOTPSecret()
if err != nil {
log.Printf("生成二级TOTP密钥失败: %v", err)
wdd_log.Error("生成二级TOTP密钥失败: %v", err)
return
}
service.authorizationInfo.SecondTOTPSecret = secondTOTPSecret
@@ -63,7 +69,7 @@ func NewAuthService() *AuthService {
// 持久化写入到授权文件中
err = service.saveAuthorizationInfo()
if err != nil {
log.Printf("持久化写入授权文件失败: %v", err)
wdd_log.Error("持久化写入授权文件失败: %v", err)
return
}
}
@@ -80,8 +86,13 @@ func (as *AuthService) AddHostInfo(hostInfo models2.HostInfo) {
as.mu.Lock()
defer as.mu.Unlock()
hostKey := utils.GenerateHostKey(hostInfo)
as.hostInfoSet[hostKey] = hostInfo
hostKey, err := totp_tier_one.EncryptHostInfo(hostInfo, as.totpService.tierOneSecret)
if err != nil {
wdd_log.Error("加密主机信息失败: %v", err)
return
}
as.heartBeatHostInfoMap[hostKey] = hostInfo
}
// GenerateAuthorizationFile 生成授权文件
@@ -90,96 +101,143 @@ func (as *AuthService) GenerateAuthorizationFile() (*models2.AuthorizationFile,
defer as.mu.RUnlock()
// 检查是否有主机信息
if len(as.hostInfoSet) == 0 {
if len(as.heartBeatHostInfoMap) == 0 {
return nil, errors.New("没有可用的主机信息")
}
// 生成TOTP验证码
totpCode, err := as.totpService.GenerateTierTwoTOTPCode(as.authorizationInfo.SecondTOTPSecret)
// 加密主机信息
encryptedHostMap := make(map[string]models2.HostInfo)
for _, hostInfo := range as.heartBeatHostInfoMap {
encrypted, err := totp_tier_one.EncryptHostInfo(hostInfo, as.totpService.tierOneSecret)
if err != nil {
wdd_log.Error("加密主机信息失败: %v hostInfo NetworkInfo: %v", err, hostInfo.NetInfo)
}
encryptedHostMap[encrypted] = hostInfo
}
// 获取项目命名空间
projectNamespace := config.GetConfig().Project.ProjectNamespace
encryptedNamespace, err := totp_tier_one.Encrypt(projectNamespace, projectNamespace)
if err != nil {
return nil, err
}
// 获取当前时间
now := time.Now()
// 准备初次授权时间
firstAuthTime := now
if as.initialized {
firstAuthTime = as.authorizationInfo.FirstAuthTime
}
// 计算时间偏移
timeOffset := now.Unix() - firstAuthTime.Unix()
// 加密主机信息
encryptedHosts := make([]string, 0)
for _, hostInfo := range as.hostInfoSet {
encrypted, err := utils.EncryptHostInfo(hostInfo)
if err != nil {
return nil, err
}
encryptedHosts = append(encryptedHosts, encrypted)
// 生成TOTP验证码
tierOneSecret := config.GetConfig().TierOneAuth.TierOneSecret
tierOneTOTPCode, err := totp_tier_one.GenerateTierOneTOTPCode(tierOneSecret)
if err != nil {
wdd_log.Error("生成TOTP验证码失败: %v", err)
return nil, err
}
// 创建授权文件
authFile := &models2.AuthorizationFile{
EncryptedHosts: encryptedHosts,
TOTPCode: totpCode,
CurrentTime: now,
FirstAuthTime: firstAuthTime,
TimeOffset: timeOffset,
EncryptedHostMap: encryptedHostMap,
TOTPCode: tierOneTOTPCode,
CurrentTime: utils.CurentTimeString(),
ProjectNamespace: projectNamespace,
EncryptedNamespace: encryptedNamespace,
}
return authFile, nil
}
// ProcessAuthorizationCode 处理授权码
func (as *AuthService) ProcessAuthorizationCode(code models2.AuthorizationCode) error {
func (as *AuthService) ProcessAuthorizationCode(authCode models2.AuthorizationCode) error {
as.mu.Lock()
defer as.mu.Unlock()
// 验证TOTP
if !as.totpService.VerifyTierTwoTOTPCode(code.TOTPCode, as.authorizationInfo.SecondTOTPSecret) {
if !as.totpService.VerifyTierOneTOTP(authCode.TOTPCode) {
wdd_log.Error("无效的授权码: TOTP验证失败")
return errors.New("无效的授权码: TOTP验证失败")
}
// 解密项目命名空间
projectNamespace, err := totp_tier_one.Decrypt(authCode.EncryptedNamespace, authCode.ProjectNamespace)
if err != nil {
wdd_log.Error("解密项目命名空间失败: %v", err)
return err
}
// 验证项目命名空间
if projectNamespace != config.GetConfig().Project.ProjectNamespace {
wdd_log.Error("无效的授权码: 项目命名空间不匹配")
return errors.New("无效的授权码: 项目命名空间不匹配")
}
// 解密服务器信息
encryptedServerInfoMap := authCode.EncryptedHostMap
serverInfoMap := make(map[string]models2.HostInfo)
for encrypted, hostInfo := range encryptedServerInfoMap {
isOK, err := totp_tier_one.DecryptHostInfo(encrypted, hostInfo, as.totpService.tierOneSecret)
if !isOK || err != nil {
wdd_log.Error("解密服务器信息失败: %v hostInfo NetworkInfo: %v", err, hostInfo.NetInfo)
continue
}
// 将解密后的主机信息添加到serverInfoMap中
serverInfoMap[encrypted] = hostInfo
// 将解密后的主机信息添加到hostInfoSet中
as.heartBeatHostInfoMap[encrypted] = hostInfo
}
// 获取当前时间
now := time.Now()
now := utils.CurentTimeString()
// 准备初次授权时间
firstAuthTime := now
if as.initialized {
firstAuthTime = as.authorizationInfo.FirstAuthTime
if as.authorizationInfo.FirstAuthTime != "" {
wdd_log.Debug("已存在初次授权时间: %s", as.authorizationInfo.FirstAuthTime)
firstAuthTime = as.authorizationInfo.FirstAuthTime
}
}
// 计算授权时间偏移
timeOffset := now.Unix() - code.CurrentTime.Unix()
nowTime, err := utils.ParseTimeString(now)
if err != nil {
wdd_log.Error("解析当前时间失败: %v", err)
return err
}
firstAuthTimeTime, err := utils.ParseTimeString(firstAuthTime)
if err != nil {
wdd_log.Error("解析初次授权时间失败: %v", err)
return err
}
timeOffset := nowTime.Unix() - firstAuthTimeTime.Unix()
// 加密授权码
authCodeJSON, err := json.Marshal(code)
authCodeJSON, err := json.Marshal(authCode)
if err != nil {
wdd_log.Error("加密授权码失败: %v", err)
return err
}
encryptedCode, err := utils.Encrypt(string(authCodeJSON), config.GetConfig().Auth.Secret)
// 加密授权码 全部加密
encryptedCode, err := totp_tier_one.Encrypt(string(authCodeJSON), as.totpService.tierOneSecret)
if err != nil {
wdd_log.Error("加密授权码失败: %v", err)
return err
}
// 保存授权信息
as.authorizationInfo = models2.AuthorizationStorage{
EncryptedCode: encryptedCode,
FirstAuthTime: firstAuthTime,
TimeOffset: timeOffset,
AuthorizedHosts: code.EncryptedHosts,
as.authorizationInfo = &models2.AuthorizationStorage{
EncryptedAuthrizationCode: encryptedCode,
FirstAuthTime: firstAuthTime,
TimeOffset: timeOffset,
AuthorizedHostMap: serverInfoMap,
SecondTOTPSecret: as.authorizationInfo.SecondTOTPSecret,
ProjectNamespace: projectNamespace,
EncryptedNamespace: authCode.EncryptedNamespace,
}
// 保存到文件
if err := as.saveAuthorizationInfo(); err != nil {
wdd_log.Error("保存授权信息失败: %v", err)
return err
}
// 设置初始化状态
as.initialized = true
return nil
@@ -195,19 +253,14 @@ func (as *AuthService) IsHostAuthorized(hostInfo models2.HostInfo) bool {
}
// 加密主机信息
encrypted, err := utils.EncryptHostInfo(hostInfo)
encrypted, err := totp_tier_one.EncryptHostInfo(hostInfo, as.totpService.tierOneSecret)
if err != nil {
return false
}
// 检查是否在已授权列表中
for _, host := range as.authorizationInfo.AuthorizedHosts {
if host == encrypted {
return true
}
}
return false
_, ok := as.authorizationInfo.AuthorizedHostMap[encrypted]
return ok
}
// VerifyAuthorizationTime 验证授权时间有效性
@@ -220,43 +273,87 @@ func (as *AuthService) VerifyAuthorizationTime() {
}
// 获取当前时间和存储的初次授权时间
now := time.Now()
now := utils.CurentTimeString()
storedOffset := as.authorizationInfo.TimeOffset
nowTime, err := utils.ParseTimeString(now)
if err != nil {
return
}
firstAuthTime, err := utils.ParseTimeString(as.authorizationInfo.FirstAuthTime)
if err != nil {
return
}
// 计算实际时间偏移
actualOffset := now.Unix() - as.authorizationInfo.FirstAuthTime.Unix()
actualOffset := nowTime.Unix() - firstAuthTime.Unix()
// 计算偏差
offsetDiff := actualOffset - storedOffset
// 获取允许的时间偏移
allowedOffset := config.GetConfig().Auth.TimeOffsetAllowed
allowedOffset := config.GetConfig().TierOneAuth.TimeOffsetAllowed
// 检查偏差是否超过允许范围
if offsetDiff > allowedOffset || offsetDiff < -allowedOffset {
log.Printf("检测到时间篡改! 存储偏移: %d, 实际偏移: %d, 差值: %d",
wdd_log.Warn("检测到时间篡改! 存储偏移: %d, 实际偏移: %d, 差值: %d",
storedOffset, actualOffset, offsetDiff)
// 这里可以添加更多的处理逻辑,例如发送警告、禁用授权等
}
}
// GetAuthorizationInfo 获取授权信息
func (as *AuthService) GetAuthorizationInfo() interface{} {
func (as *AuthService) GetAuthorizationInfo() (models2.AuthorizationStorage, error) {
as.mu.RLock()
defer as.mu.RUnlock()
if !as.initialized {
return map[string]interface{}{
"authorized": false,
"message": "未授权",
}
return models2.AuthorizationStorage{}, errors.New("未授权")
}
return map[string]interface{}{
"authorized": true,
"authorized_hosts": len(as.authorizationInfo.AuthorizedHosts),
"first_auth_time": as.authorizationInfo.FirstAuthTime,
// 解密授权码
decryptedCode, err := totp_tier_one.Decrypt(as.authorizationInfo.EncryptedAuthrizationCode, as.totpService.tierOneSecret)
if err != nil {
return models2.AuthorizationStorage{}, err
}
// 解析授权码
var authorizationInfo models2.AuthorizationStorage
if err := json.Unmarshal([]byte(decryptedCode), &authorizationInfo); err != nil {
return models2.AuthorizationStorage{}, err
}
return authorizationInfo, nil
}
// GetAllHostInfo 获取所有主机信息(心跳检测到的所有主机)
func (as *AuthService) GetAllHostInfo() map[string]models2.HostInfo {
as.mu.RLock()
defer as.mu.RUnlock()
// 返回主机信息集合的副本
result := make(map[string]models2.HostInfo, len(as.heartBeatHostInfoMap))
for k, v := range as.heartBeatHostInfoMap {
result[k] = v
}
return result
}
// GetAllAuthorizedHosts 获取所有已授权的主机信息
func (as *AuthService) GetAllAuthorizedHosts() map[string]models2.HostInfo {
as.mu.RLock()
defer as.mu.RUnlock()
if !as.initialized || as.authorizationInfo == nil {
return make(map[string]models2.HostInfo)
}
// 返回已授权主机信息的副本
result := make(map[string]models2.HostInfo, len(as.authorizationInfo.AuthorizedHostMap))
for k, v := range as.authorizationInfo.AuthorizedHostMap {
result[k] = v
}
return result
}
// saveAuthorizationInfo 保存授权信息到文件
@@ -266,30 +363,40 @@ func (as *AuthService) saveAuthorizationInfo() error {
return err
}
return os.WriteFile(config.GetConfig().Auth.AuthFilePath, data, 0600)
return os.WriteFile(config.GetConfig().TierOneAuth.LocalAuthFilePath, data, 0600)
}
// loadAuthorizationInfo 从文件加载授权信息
func (as *AuthService) loadAuthorizationInfo() {
filePath := config.GetConfig().Auth.AuthFilePath
filePath := config.GetConfig().TierOneAuth.LocalAuthFilePath
// 检查文件是否存在
if _, err := os.Stat(filePath); os.IsNotExist(err) {
// 初始化一个空的授权信息
as.authorizationInfo = &models2.AuthorizationStorage{}
return
}
data, err := os.ReadFile(filePath)
if err != nil {
log.Printf("读取授权文件失败: %v", err)
wdd_log.Error("读取授权文件失败: %v", err)
// 初始化一个空的授权信息
as.authorizationInfo = &models2.AuthorizationStorage{}
return
}
var authInfo models2.AuthorizationStorage
var authInfo *models2.AuthorizationStorage
if err := json.Unmarshal(data, &authInfo); err != nil {
log.Printf("解析授权文件失败: %v", err)
wdd_log.Error("解析授权文件失败: %v", err)
// 初始化一个空的授权信息
as.authorizationInfo = &models2.AuthorizationStorage{}
return
}
if authInfo == nil {
authInfo = &models2.AuthorizationStorage{}
}
as.authorizationInfo = authInfo
as.initialized = true
}