Maison  >  Article  >  développement back-end  >  Golang implémente la connexion SSO

Golang implémente la connexion SSO

PHPz
PHPzoriginal
2023-05-10 11:33:361178parcourir

Avec le développement d'Internet, de plus en plus de sites Web et d'applications doivent implémenter une fonctionnalité d'authentification unique (SSO) pour offrir une meilleure expérience utilisateur et une sécurité accrue. Dans cet article, nous présenterons comment implémenter la connexion SSO à l'aide du langage Go.

1. Qu'est-ce que l'authentification unique ?

Single Sign-On (SSO) est un protocole d'authentification qui permet aux utilisateurs d'utiliser un seul nom d'utilisateur et un seul mot de passe pour accéder à plusieurs applications, plutôt que de s'authentifier individuellement pour chaque application. Dans un système SSO, les utilisateurs doivent uniquement fournir leurs informations d'identification lors de leur première connexion, et ils sont automatiquement connectés lors des connexions suivantes.

Le principe de base du SSO est de partager les informations d'identification entre différents domaines. Dans ce cas, les informations d'identification de l'utilisateur (telles que le nom d'utilisateur et le mot de passe) doivent être vérifiées uniquement dans un domaine et peuvent ensuite être utilisées pour accéder aux applications d'autres domaines. Ce processus peut simplifier le processus de connexion de l'utilisateur, réduire la charge de travail de l'utilisateur et améliorer la sécurité.

2. Comment mettre en œuvre le SSO ?

Afin de mettre en œuvre la connexion SSO, nous devons définir un centre d'authentification public (Authentication Center) qui communique avec chaque application. L'autorité d'authentification est chargée de valider les informations d'identification de l'utilisateur, puis de fournir un jeton à chaque application pour permettre à l'utilisateur d'accéder à l'application. Lors de visites ultérieures, l'utilisateur peut utiliser le jeton pour s'authentifier sans fournir à nouveau ses informations d'identification.

En langage Go, nous pouvons utiliser le framework Web léger gin pour implémenter la connexion SSO. Voici une architecture SSO de base :

SSO Architecture

Dans la figure ci-dessus, nous définissons un centre de certification et deux applications (App1 et App2), qui implémentent la connexion SSO via le centre de certification. Nous utilisons JWT (JSON Web Token) pour représenter les jetons car il s'agit d'un standard léger qui peut utiliser JSON pour décrire et transmettre des informations.

3. Comment y parvenir ?

  1. Installer GIN

Tout d'abord, nous devons installer le framework Gin. Entrez la commande suivante sur le terminal pour installer gin :

go get -u github.com/gin-gonic/gin
  1. Écrire le centre de certification

Ensuite, nous devons écrire le code du centre de certification. Dans cet exemple, nous utiliserons le framework Gin pour créer un serveur HTTP. Nous devons connecter l'utilisateur sur le serveur et créer un jeton JWT pour représenter l'utilisateur. Le token sera restitué à l'utilisateur après authentification et sera vérifié lors des demandes ultérieures.

package main

import (
  "net/http"
  "time"

  "github.com/gin-gonic/gin"
  "github.com/golang-jwt/jwt"
)

var jwtKey = []byte("my_secret_key")

type Credentials struct {
  Username string `json:"username"`
  Password string `json:"password"`
}

type Claims struct {
  Username string `json:"username"`
  jwt.StandardClaims
}

func main() {
  router := gin.Default()

  router.POST("/login", loginHandler)

  router.GET("/validate", validateHandler)

  router.Run(":8080")
}

func loginHandler(c *gin.Context) {
  var creds Credentials
  if err := c.BindJSON(&creds); err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request payload"})
    return
  }

  if creds.Username != "user" || creds.Password != "password" {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
    return
  }

  expirationTime := time.Now().Add(5 * time.Minute)
  claims := &Claims{
    Username: creds.Username,
    StandardClaims: jwt.StandardClaims{
      ExpiresAt: expirationTime.Unix(),
    },
  }

  token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  tokenString, err := token.SignedString(jwtKey)
  if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
    return
  }

  c.JSON(http.StatusOK, gin.H{"token": tokenString})
}

func validateHandler(c *gin.Context) {
  tokenString := c.Request.Header.Get("Authorization")
  if tokenString == "" {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
    return
  }

  token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
    if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
      return nil, jwt.ErrSignatureInvalid
    }
    return jwtKey, nil
  })

  if err != nil {
    c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
    return
  }

  if !token.Valid {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
    return
  }

  claims, ok := token.Claims.(*Claims)
  if !ok {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Failed to parse claims"})
    return
  }

  c.JSON(http.StatusOK, gin.H{"username": claims.Username})
}

Dans le centre de certification, nous avons défini deux routes "/login" et "/validate".

Sur la route "/login", nous lisons les informations d'identification et les validons (juste un exemple simple ici), et si la validation réussit, nous créons un jeton JWT et le renvoyons au client.

Sur la route "/validate", nous lisons le token JWT et le validons en utilisant la même clé secrète. Si le jeton est valide, nous extrayons les réclamations qu'il contient et les renvoyons au client.

  1. Écrire l'application

Ensuite, nous devons écrire le code de l'application. Dans cet exemple, nous utiliserons le framework Gin pour créer un serveur HTTP. Nous devons accéder à l'autorité d'authentification pour vérifier les informations d'identification de l'utilisateur et obtenir le jeton JWT après vérification. Le jeton sera stocké dans un cookie et vérifié lors des demandes ultérieures.

package main

import (
  "net/http"

  "github.com/gin-gonic/gin"
  "github.com/golang-jwt/jwt"
)

var jwtKey = []byte("my_secret_key")

type Claims struct {
  Username string `json:"username"`
  jwt.StandardClaims
}

func main() {
  router := gin.Default()

  router.POST("/login", loginHandler)

  router.GET("/private", authMiddleware(), privateHandler)

  router.Run(":8081")
}

func loginHandler(c *gin.Context) {
  username := c.PostForm("username")
  password := c.PostForm("password")

  authURL := "http://localhost:8080/validate"
  client := &http.Client{}
  req, err := http.NewRequest("GET", authURL, nil)
  if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
    return
  }

  req.Header.Set("Authorization", c.GetHeader("Authorization"))
  res, err := client.Do(req)
  if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to validate token"})
    return
  }

  if res.StatusCode != http.StatusOK {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
    return
  }

  claims := &Claims{
    Username: username,
    StandardClaims: jwt.StandardClaims{},
  }

  token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  tokenString, err := token.SignedString(jwtKey)
  if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
    return
  }

  c.SetCookie("token", tokenString, 0, "", "", false, true)
  c.JSON(http.StatusOK, gin.H{"message": "Login success"})
}

func privateHandler(c *gin.Context) {
  claims := c.MustGet("claims").(*Claims)
  c.JSON(http.StatusOK, gin.H{"message": "You are logged in as " + claims.Username})
}

func authMiddleware() gin.HandlerFunc {
  return func(c *gin.Context) {
    tokenString, err := c.Cookie("token")
    if err != nil {
      c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
      c.Abort()
      return
    }

    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
      if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
        return nil, jwt.ErrSignatureInvalid
      }
      return jwtKey, nil
    })

    if err != nil {
      c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
      c.Abort()
      return
    }

    if !token.Valid {
      c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
      c.Abort()
      return
    }

    claims, ok := token.Claims.(*Claims)
    if !ok {
      c.JSON(http.StatusUnauthorized, gin.H{"error": "Failed to parse claims"})
      c.Abort()
      return
    }

    c.Set("claims", claims)
    c.Next()
  }
}

Dans l'application, nous avons défini deux routes "/login" et "/private".

Sur la route "/login", nous faisons une requête GET à l'autorité d'authentification et ajoutons le token JWT dans l'en-tête de la requête pour vérification. Si la vérification réussit, nous créons un jeton JWT et le stockons dans un cookie.

Sur la route "/private", nous utilisons un middleware pour vérifier que le token JWT est inclus dans la requête. Si le jeton est valide, nous en extrairons les réclamations et les traiterons en conséquence.

4. Résumé

Dans cet article, nous avons présenté comment utiliser le langage Go pour implémenter la fonction d'authentification unique (SSO). Nous avons utilisé le framework Gin pour créer le serveur HTTP et utilisé des jetons JWT pour représenter les utilisateurs. Nous définissons une autorité de certification et l'utilisons pour valider les demandes et émettre des jetons. Nous avons également codé l'application et utilisé un middleware pour l'authentification. Cet exemple n’est qu’un exemple simple et peut être étendu pour répondre aux besoins spécifiques des applications.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn