Authentification invité pour un serveur de jeu Mahjong Golang avec DevOps et Kubernetes

Analyse des fonctionnalités pour l'authentification invité

Dans le développement d'un serveur de jeu Mahjong, l'authentification invité permet aux utilisateurs de s'identifier sans compte préalable, généralement via un identifiant unique comme l'IMEI d'un appareil. Ce processus implique la vérification de l'état d'activation, la création d'un utilisateur si nécessaire, et la gestion des sessions.

Points d'API pour l'authentification invité

Deux endpoints principaux sont nécessaires : l'un pour interroger si l'authentification invité est activée, et l'autre pour effectuer la connexion.

Interrogation de l'état d'activation

Cet endpoint vérifie la configuration du serveur pour détemriner si l'authentification invité est autorisée pour un canal et une application spécifiques.


POST /v1/user/login/query HTTP/1.1
Content-Type: application/json

{
  "applicationId": "testApp",
  "channel": "mobile"
}

La réponse indique si l'authentification invité est active :


HTTP/1.1 200 OK
Content-Type: application/json

{
  "status": 0,
  "guestEnabled": true
}

Connexion invité

Cet endpoint gère la connexion des invités en créant ou récupérant un utilisateur basé sur l'identifiant de l'appareil.


POST /v1/user/login/guest HTTP/1.1
Content-Type: application/json

{
  "applicationId": "testApp",
  "channel": "mobile",
  "deviceId": "a1b2c3d4e5f6"
}

La réponse contient les détails de l'utilisateur et la configuration du client :


{
  "code": 0,
  "username": "Guest_123",
  "userId": 1001,
  "avatarUrl": "https://example.com/avatar.png",
  "virtualCurrency": 10,
  "gender": 1,
  "ipAddress": "192.168.1.100",
  "port": 45678,
  "clientConfig": {
    "appVersion": "2.0.0",
    "updateLinks": {
      "android": "https://example.com/app.apk",
      "ios": "https://example.com/app.ipa"
    },
    "keepAliveInterval": 30,
    "forceUpdate": false,
    "shareTitle": "Jouez au Mahjong",
    "shareDescription": "Un jeu de Mahjong en ligne",
    "supportContacts": {
      "agent1": "support1@example.com",
      "agent2": "support2@example.com",
      "customerService": "service@example.com"
    },
    "applicationKey": "appSecretKey"
  },
  "notifications": ["Bienvenue dans le jeu!"],
  "clubs": []
}

Logique métier détaillée

L'implémentation de l'authentification invité repose sur plusieurs étapes critiques, intégrant des vérifications de base de données et la gestion des données utilisateur.

Modèles de données

Les structures suivantes représentent les tables nécessaires dans la base de données, avec des annotations pour l'ORM utilisé (par exemple, GORM en Golang).


type Player struct {
    ID            int64  `gorm:"primaryKey;autoIncrement"`
    EncryptionAlgo string `gorm:"type:varchar(16);not null;default:''"`
    PasswordHash   string `gorm:"type:varchar(64);not null;default:''"`
    SaltValue      string `gorm:"type:varchar(64);not null;default:''"`
    AccountRole    int    `gorm:"type:tinyint;not null;default:1"`
    AccountStatus  int    `gorm:"type:tinyint;not null;default:1"`
    ActiveStatus   int    `gorm:"type:tinyint;not null;default:1"`
    LastAccessTime int64  `gorm:"index;not null;default:0"`
    PrivateKey     string `gorm:"type:varchar(512);not null;default:''"`
    PublicKey      string `gorm:"type:varchar(128);not null;default:''"`
    Balance        int64  `gorm:"not null;default:0"`
    CreationTime   int64  `gorm:"index;not null;default:0"`
    FirstPaymentTime int64 `gorm:"index;not null;default:0"`
    DebugFlag      int    `gorm:"type:tinyint;not null;default:0"`
}

type RegistrationRecord struct {
    RecordID       int64  `gorm:"primaryKey;autoIncrement"`
    PlayerID       int64  `gorm:"index;not null"`
    ExternalIP     string `gorm:"type:varchar(40);not null;default:''"`
    InternalIP     string `gorm:"type:varchar(40);not null;default:''"`
    DeviceIMEI     string `gorm:"type:varchar(128);not null;default:''"`
    OperatingSystem string `gorm:"type:varchar(20);not null;default:''"`
    HardwareModel  string `gorm:"type:varchar(20);not null;default:''"`
    AppIdentifier  string `gorm:"index;type:varchar(32);not null;default:''"`
    ChannelID      string `gorm:"index;type:varchar(32);not null;default:''"`
    RegistrationTime int64 `gorm:"index;not null;default:0"`
    RegistrationType int  `gorm:"type:tinyint;not null;default:0"`
}

type SessionLog struct {
    LogID        int64  `gorm:"primaryKey;autoIncrement"`
    PlayerID     int64  `gorm:"index;not null"`
    ExternalIP   string `gorm:"type:varchar(40);not null;default:''"`
    InternalIP   string `gorm:"type:varchar(40);not null;default:''"`
    HardwareModel string `gorm:"type:varchar(64);not null;default:''"`
    DeviceIMEI   string `gorm:"type:varchar(32);not null;default:''"`
    OSVersion    string `gorm:"type:varchar(64);not null;default:''"`
    AppIdentifier string `gorm:"type:varchar(64);not null;default:''"`
    ChannelID    string `gorm:"type:varchar(32);not null;default:''"`
    LoginTime    int64  `gorm:"not null;default:0"`
    LogoutTime   int64  `gorm:"not null;default:0"`
}

Étapes de traitement

  1. Vérifier si l'utilisateur invité existe déjà dans la base de données en utilisant l'identifiant de l'application et l'IMEI de l'appareil. La fonction FindPlayerByDevice interroge les tables d'enregistrement et de joueur.

    
    player, err := database.FindPlayerByDevice(req.ApplicationID, req.Device.IMEI)
    
    
  2. Si aucun utilisateur n'est trouvé, créer un nouveau joueur avec des valeurs par défaut, comme un solde initial de 10 unités de monnaie virtuelle.

    
    newPlayer := &Player{
       AccountStatus: 1,
       ActiveStatus:  1,
       AccountRole:   2,
       Balance:       10,
    }
    insertErr := database.InsertPlayer(newPlayer)
    
    

    Ensuite, enregistrer une entrée dans la table des enregistrements pour le suivi.

    
    registrationData := RegistrationRecord{
       PlayerID:       newPlayer.ID,
       DeviceIMEI:     req.Device.IMEI,
       AppIdentifier:  req.ApplicationID,
       ChannelID:      req.Channel,
       RegistrationTime: time.Now().Unix(),
       RegistrationType: 5, // Type pour les comptes invités
    }
    database.InsertRegistrationRecord(&registrationData)
    
    
  3. Construire la réponse de connexion avec les informations du joueur, la configuration du client et les messages de notification.

    
    response := LoginResponse{
       Code:     0,
       Username: fmt.Sprintf("Guest_%d", player.ID),
       UserID:   player.ID,
       AvatarURL: defaultAvatarURL,
       Balance:  player.Balance,
       Gender:   1,
       IPAddress: extractIP(req.Device.Remote),
       Port:     serverPort,
       ClientConfig: loadClientConfig(),
       Notifications: systemMessages,
       Clubs:    emptyClubList,
    }
    
    
  4. Enregistrer la session de connexion dans la table des journaux pour l'audit.

    
    sessionLog := SessionLog{
       PlayerID:   player.ID,
       ExternalIP: req.Device.Remote,
       InternalIP: req.Device.InternalIP,
       LoginTime:  time.Now().Unix(),
    }
    database.InsertSessionLog(&sessionLog)
    
    

Structures de données pour les requêtes et réponses

Définitions des types utilisés dans le protocole de communication.


type GuestLoginRequest struct {
    ApplicationID string `json:"applicationId"`
    Channel       string `json:"channel"`
    Device        DeviceInfo `json:"device"`
}

type DeviceInfo struct {
    IMEI       string `json:"imei"`
    OS         string `json:"os"`
    Model      string `json:"model"`
    InternalIP string `json:"internalIP"`
    Remote     string `json:"remote"`
}

type LoginResponse struct {
    Code         int          `json:"code"`
    Username     string       `json:"username"`
    UserID       int64        `json:"userId"`
    AvatarURL    string       `json:"avatarUrl"`
    Balance      int64        `json:"balance"`
    Gender       int          `json:"gender"`
    IPAddress    string       `json:"ipAddress"`
    Port         int          `json:"port"`
    ClientConfig ClientConfig `json:"clientConfig"`
    Notifications []string    `json:"notifications"`
    Clubs        []ClubInfo   `json:"clubs"`
}

type ClientConfig struct {
    AppVersion       string            `json:"appVersion"`
    UpdateLinks      map[string]string `json:"updateLinks"`
    KeepAliveInterval int             `json:"keepAliveInterval"`
    ForceUpdate      bool              `json:"forceUpdate"`
    ShareTitle       string            `json:"shareTitle"`
    ShareDescription string            `json:"shareDescription"`
    SupportContacts  map[string]string `json:"supportContacts"`
    ApplicationKey   string            `json:"applicationKey"`
}

type ClubInfo struct {
    ClubID       int64  `json:"clubId"`
    ClubName     string `json:"clubName"`
    Description  string `json:"description"`
    MemberCount  int    `json:"memberCount"`
    MaxMembers   int    `json:"maxMembers"`
}

Cette approche garantit une authentification invité sécurisée et évolutible, intégrée dans une architecture cloud-native avec des pratiques DevOps.

Étiquettes: golang kubernetes devops GitOps SRE

Publié le 25 juin à 17h58