隨著網路的發展,越來越多的網站和應用程式需要實現單一登入(SSO)功能,以提供更好的使用者體驗和更高的安全性。在本文中,我們將介紹如何使用Go語言實作SSO登入。
一、什麼是SSO?
單一登入(SSO)是一種身份驗證協議,它允許使用者使用一個使用者名稱和密碼來存取多個應用程序,而不是針對每個應用程式進行單獨認證。在SSO系統中,使用者只需在第一次登入時提供憑證,然後在後續的登入中自動登入。
SSO的核心原理是在不同的領域之間共享憑證。在這種情況下,使用者的憑證(如使用者名稱和密碼)只需要在一個網域中進行驗證,然後可以用來存取其他網域中的應用程式。這個過程可以簡化使用者的登入流程,減少使用者的負擔,提高安全性。
二、如何實作SSO?
為了實現SSO登錄,我們需要定義一個公共的認證中心(Authentication Center),它與各個應用程式之間通訊。認證中心負責驗證使用者的憑證,然後向每個應用程式提供令牌(Token)來允許使用者存取該應用程式。在後續的存取中,使用者可以使用令牌進行認證,而無需再次提供憑證。
在Go語言中,我們可以使用輕量級的web框架gin來實作SSO登入。下面是一個基本的SSO架構:
在上圖中,我們定義了一個認證中心和兩個應用程式(App1和App2),它們之間通過認證中心實現SSO登入。我們使用JWT(JSON Web Token)來表示令牌,因為它是一種輕量級的標準,可以使用JSON來描述和傳輸訊息。
三、如何實現?
首先,我們需要安裝Gin框架。在終端機上輸入以下命令以安裝gin:
go get -u github.com/gin-gonic/gin
#接下來,我們需要編寫認證中心的程式碼。在這個例子中,我們將使用Gin框架來建立一個HTTP伺服器。我們需要在伺服器上讓使用者登錄,並建立一個JWT令牌來表示使用者。令牌將在認證後返回給用戶,並在後續的請求中進行驗證。
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}) }
在認證中心中,我們定義了兩個路由「/login」和「/validate」。
在「/login」路由上,我們讀取憑證並進行驗證(這裡只是一個簡單的範例),如果驗證通過,我們建立一個JWT令牌,並將其發送回客戶端。
在「/validate」路由上,我們讀取JWT令牌,並使用相同的秘鑰對其進行驗證。如果令牌有效,我們將提取其中的聲明,並將其發送回客戶端。
接下來,我們需要寫應用程式的程式碼。在這個範例中,我們將使用Gin框架來建立HTTP伺服器。我們需要訪問認證中心來驗證使用者的憑證,並在驗證後取得JWT令牌。令牌將儲存在Cookie中,並在後續的請求中進行驗證。
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() } }
在應用程式中,我們定義了兩個路由「/login」和「/private」。
在「/login」路由上,我們向認證中心發出GET請求,並在請求頭中新增JWT令牌以進行驗證。如果驗證通過,我們將建立一個JWT令牌,並將其儲存在Cookie中。
在「/private」路由上,我們使用中間件來驗證請求中是否包含JWT令牌。如果令牌有效,我們將提取其中的聲明,並進行相應的處理。
四、總結
在本文中,我們介紹如何使用Go語言實現單一登入(SSO)功能。我們使用了Gin框架來建立HTTP伺服器,並使用JWT令牌來表示使用者。我們定義了一個認證中心,並將其用於驗證請求和頒發令牌。我們也編寫了應用程式的程式碼,並使用中間件來進行身份驗證。這個例子只是一個簡單的範例,可以擴展以滿足特定的應用程式需求。
以上是golang實作sso登錄的詳細內容。更多資訊請關注PHP中文網其他相關文章!