.
This commit is contained in:
215
internal/service/auth_service.go
Normal file
215
internal/service/auth_service.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"ssq-desk/internal/database"
|
||||
"ssq-desk/internal/storage/models"
|
||||
"ssq-desk/internal/storage/repository"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AuthService 授权服务
|
||||
type AuthService struct {
|
||||
repo repository.AuthRepository
|
||||
}
|
||||
|
||||
// NewAuthService 创建授权服务
|
||||
func NewAuthService(repo repository.AuthRepository) *AuthService {
|
||||
return &AuthService{repo: repo}
|
||||
}
|
||||
|
||||
// GetDeviceID 获取设备ID(基于硬件信息生成)
|
||||
func GetDeviceID() (string, error) {
|
||||
var deviceInfo string
|
||||
|
||||
// 获取主机名
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
hostname = "unknown"
|
||||
}
|
||||
|
||||
// 获取用户目录
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
homeDir = "unknown"
|
||||
}
|
||||
|
||||
// 组合设备信息
|
||||
deviceInfo = fmt.Sprintf("%s-%s-%s", hostname, homeDir, runtime.GOOS)
|
||||
|
||||
// 生成 MD5 作为设备ID
|
||||
hash := md5.Sum([]byte(deviceInfo))
|
||||
deviceID := hex.EncodeToString(hash[:])
|
||||
|
||||
return deviceID, nil
|
||||
}
|
||||
|
||||
// ValidateLicenseFormat 验证授权码格式
|
||||
func ValidateLicenseFormat(licenseCode string) error {
|
||||
if licenseCode == "" {
|
||||
return fmt.Errorf("授权码不能为空")
|
||||
}
|
||||
|
||||
// 去除空格和连字符
|
||||
cleaned := ""
|
||||
for _, c := range licenseCode {
|
||||
if c != ' ' && c != '-' {
|
||||
cleaned += string(c)
|
||||
}
|
||||
}
|
||||
|
||||
// 格式验证:至少16位,只包含字母和数字
|
||||
if len(cleaned) < 16 {
|
||||
return fmt.Errorf("授权码长度不足,至少需要16位字符")
|
||||
}
|
||||
|
||||
if len(cleaned) > 100 {
|
||||
return fmt.Errorf("授权码长度过长,最多100位字符")
|
||||
}
|
||||
|
||||
// 验证字符:只允许字母和数字
|
||||
for _, c := range cleaned {
|
||||
if !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
|
||||
return fmt.Errorf("授权码只能包含字母和数字")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateLicenseFromRemote 从远程数据库验证授权码
|
||||
func ValidateLicenseFromRemote(licenseCode string) error {
|
||||
// 获取 MySQL 连接
|
||||
mysqlDB := database.GetMySQL()
|
||||
if mysqlDB == nil {
|
||||
return fmt.Errorf("无法连接远程数据库,无法验证授权码")
|
||||
}
|
||||
|
||||
// 清理授权码(去除空格和连字符),与格式验证保持一致
|
||||
cleaned := ""
|
||||
for _, c := range licenseCode {
|
||||
if c != ' ' && c != '-' {
|
||||
cleaned += string(c)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询授权码是否存在且有效(支持原始格式和清理后格式)
|
||||
var auth models.Authorization
|
||||
err := mysqlDB.Where("(license_code = ? OR license_code = ?) AND status = ?", licenseCode, cleaned, 1).First(&auth).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return fmt.Errorf("授权码无效或不存在")
|
||||
}
|
||||
return fmt.Errorf("验证授权码时发生错误: %v", err)
|
||||
}
|
||||
|
||||
// 检查是否过期
|
||||
if auth.ExpiresAt != nil && auth.ExpiresAt.Before(time.Now()) {
|
||||
return fmt.Errorf("授权码已过期")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateLicense 验证授权码
|
||||
func (s *AuthService) ValidateLicense(licenseCode string) error {
|
||||
// 格式验证
|
||||
if err := ValidateLicenseFormat(licenseCode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 从远程数据库验证授权码有效性
|
||||
if err := ValidateLicenseFromRemote(licenseCode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取设备ID
|
||||
deviceID, err := GetDeviceID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取设备ID失败: %v", err)
|
||||
}
|
||||
|
||||
// 保存授权信息到本地
|
||||
auth := &models.Authorization{
|
||||
LicenseCode: licenseCode,
|
||||
DeviceID: deviceID,
|
||||
ActivatedAt: time.Now(),
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
existing, err := s.repo.GetByLicenseCode(licenseCode)
|
||||
if err == nil && existing != nil {
|
||||
// 更新现有授权
|
||||
existing.DeviceID = deviceID
|
||||
existing.ActivatedAt = time.Now()
|
||||
existing.Status = 1
|
||||
return s.repo.Update(existing)
|
||||
}
|
||||
|
||||
// 创建新授权
|
||||
return s.repo.Create(auth)
|
||||
}
|
||||
|
||||
// CheckAuthStatus 检查授权状态
|
||||
func (s *AuthService) CheckAuthStatus() (*AuthStatus, error) {
|
||||
deviceID, err := GetDeviceID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取设备ID失败: %v", err)
|
||||
}
|
||||
|
||||
auth, err := s.repo.GetByDeviceID(deviceID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return &AuthStatus{
|
||||
IsActivated: false,
|
||||
Message: "未激活",
|
||||
}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查状态
|
||||
if auth.Status != 1 {
|
||||
return &AuthStatus{
|
||||
IsActivated: false,
|
||||
Message: "授权已失效",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 检查过期时间
|
||||
if auth.ExpiresAt != nil && auth.ExpiresAt.Before(time.Now()) {
|
||||
return &AuthStatus{
|
||||
IsActivated: false,
|
||||
Message: "授权已过期",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &AuthStatus{
|
||||
IsActivated: true,
|
||||
LicenseCode: auth.LicenseCode,
|
||||
ActivatedAt: auth.ActivatedAt,
|
||||
ExpiresAt: auth.ExpiresAt,
|
||||
Message: "已激活",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AuthStatus 授权状态
|
||||
type AuthStatus struct {
|
||||
IsActivated bool `json:"is_activated"`
|
||||
LicenseCode string `json:"license_code,omitempty"`
|
||||
ActivatedAt time.Time `json:"activated_at,omitempty"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// GetAuthInfo 获取授权信息
|
||||
func (s *AuthService) GetAuthInfo() (*AuthStatus, error) {
|
||||
return s.CheckAuthStatus()
|
||||
}
|
||||
Reference in New Issue
Block a user