Rumah  >  Artikel  >  pembangunan bahagian belakang  >  golang melaksanakan log masuk sso

golang melaksanakan log masuk sso

PHPz
PHPzasal
2023-05-10 11:33:361220semak imbas

Dengan pembangunan Internet, semakin banyak tapak web dan aplikasi perlu melaksanakan fungsi log masuk tunggal (SSO) untuk memberikan pengalaman pengguna yang lebih baik dan keselamatan yang lebih tinggi. Dalam artikel ini, kami akan memperkenalkan cara melaksanakan log masuk SSO menggunakan bahasa Go.

1. Apakah itu SSO?

Single Sign-On (SSO) ialah protokol pengesahan yang membenarkan pengguna menggunakan satu nama pengguna dan kata laluan untuk mengakses berbilang aplikasi, dan bukannya mengesahkan secara individu untuk setiap aplikasi. Dalam sistem SSO, pengguna hanya perlu memberikan bukti kelayakan pada kali pertama mereka log masuk, dan mereka log masuk secara automatik pada log masuk berikutnya.

Prinsip teras SSO ialah berkongsi bukti kelayakan antara domain yang berbeza. Dalam kes ini, bukti kelayakan pengguna (seperti nama pengguna dan kata laluan) hanya perlu disahkan dalam satu domain dan kemudian boleh digunakan untuk mengakses aplikasi dalam domain lain. Proses ini boleh memudahkan proses log masuk pengguna, mengurangkan beban pengguna dan meningkatkan keselamatan.

2. Bagaimana untuk melaksanakan SSO?

Untuk melaksanakan log masuk SSO, kami perlu menentukan pusat pengesahan awam (Pusat Pengesahan) yang berkomunikasi dengan setiap aplikasi. Pihak berkuasa pengesahan bertanggungjawab untuk mengesahkan kelayakan pengguna dan kemudian memberikan token kepada setiap aplikasi untuk membolehkan pengguna mengakses aplikasi. Pada lawatan berikutnya, pengguna boleh menggunakan token untuk mengesahkan tanpa memberikan bukti kelayakan lagi.

Dalam bahasa Go, kami boleh menggunakan gin rangka kerja web ringan untuk melaksanakan log masuk SSO. Berikut ialah seni bina SSO asas:

SSO Architecture

Dalam rajah di atas, kami mentakrifkan pusat pensijilan dan dua aplikasi (App1 dan App2), yang disahkan di antara mereka Pusat melaksanakan Log masuk SSO. Kami menggunakan JWT (JSON Web Token) untuk mewakili token kerana ia adalah standard ringan yang boleh menggunakan JSON untuk menerangkan dan menghantar maklumat.

3. Bagaimana untuk mencapainya?

  1. Pasang GIN

Mula-mula, kita perlu memasang rangka kerja Gin. Masukkan arahan berikut pada terminal untuk memasang gin:

go get -u github.com/gin-gonic/gin
  1. Menulis Pusat Pensijilan

Seterusnya, kita perlu menulis kod untuk pusat pensijilan. Dalam contoh ini, kami akan menggunakan rangka kerja Gin untuk mencipta pelayan HTTP. Kami perlu melog masuk pengguna pada pelayan dan mencipta token JWT untuk mewakili pengguna. Token akan dikembalikan kepada pengguna selepas pengesahan dan akan disahkan pada permintaan seterusnya.

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})
}

Di pusat pensijilan, kami mentakrifkan dua laluan "/log masuk" dan "/validate".

Pada laluan "/log masuk", kami membaca bukti kelayakan dan mengesahkannya (hanya contoh mudah di sini), dan jika pengesahan lulus, kami mencipta token JWT dan menghantarnya semula kepada pelanggan.

Pada laluan "/validate", kami membaca token JWT dan mengesahkannya menggunakan kunci rahsia yang sama. Jika token itu sah, kami mengeluarkan tuntutan di dalamnya dan menghantarnya kembali kepada pelanggan.

  1. Menulis Aplikasi

Seterusnya, kita perlu menulis kod untuk aplikasi itu. Dalam contoh ini, kami akan menggunakan rangka kerja Gin untuk mencipta pelayan HTTP. Kami perlu mengakses pihak berkuasa pengesahan untuk mengesahkan kelayakan pengguna dan mendapatkan token JWT selepas pengesahan. Token akan disimpan dalam kuki dan disahkan pada permintaan seterusnya.

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()
  }
}

Dalam aplikasi, kami telah menentukan dua laluan "/log masuk" dan "/peribadi".

Pada laluan "/log masuk", kami membuat permintaan GET kepada pihak berkuasa pengesahan dan menambah token JWT dalam pengepala permintaan untuk pengesahan. Jika pengesahan lulus, kami mencipta token JWT dan menyimpannya dalam kuki.

Pada laluan "/private", kami menggunakan middleware untuk mengesahkan bahawa token JWT disertakan dalam permintaan. Jika token itu sah, kami akan mengeluarkan tuntutan di dalamnya dan memprosesnya dengan sewajarnya.

4. Ringkasan

Dalam artikel ini, kami memperkenalkan cara menggunakan bahasa Go untuk melaksanakan fungsi log masuk tunggal (SSO). Kami menggunakan rangka kerja Gin untuk mencipta pelayan HTTP dan menggunakan token JWT untuk mewakili pengguna. Kami mentakrifkan pihak berkuasa pensijilan dan menggunakannya untuk mengesahkan permintaan dan mengeluarkan token. Kami juga mengekodkan aplikasi dan menggunakan perisian tengah untuk pengesahan. Contoh ini hanyalah contoh mudah dan boleh dipanjangkan untuk memenuhi keperluan aplikasi tertentu.

Atas ialah kandungan terperinci golang melaksanakan log masuk sso. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:Cara merahsiakan kod golangArtikel seterusnya:Cara merahsiakan kod golang