ホームページ >バックエンド開発 >Golang >golang は sso ログインを実装します

golang は sso ログインを実装します

PHPz
PHPzオリジナル
2023-05-10 11:33:361276ブラウズ

インターネットの発展に伴い、より優れたユーザー エクスペリエンスとより高いセキュリティを提供するために、シングル サインオン (SSO) 機能を実装する必要がある Web サイトやアプリケーションがますます増えています。今回はGo言語を使ったSSOログインの実装方法を紹介します。

1.SSO とは何ですか?

シングル サインオン (SSO) は、ユーザーがアプリケーションごとに個別に認証するのではなく、単一のユーザー名とパスワードを使用して複数のアプリケーションにアクセスできるようにする認証プロトコルです。 SSO システムでは、ユーザーは初回ログイン時にのみ資格情報を入力する必要があり、それ以降のログインでは自動的にログインされます。

SSO の中心原則は、異なるドメイン間で資格情報を共有することです。この場合、ユーザーの資格情報 (ユーザー名やパスワードなど) は 1 つのドメインで検証するだけで済み、その後、その資格情報を使用して他のドメインのアプリケーションにアクセスできます。この処理により、ユーザーのログイン処理が簡素化され、ユーザーの負担が軽減され、セキュリティが向上します。

2. SSO を実装するにはどうすればよいですか?

SSO ログインを実装するには、各アプリケーションと通信する公的認証センター (Authentication Center) を定義する必要があります。認証局は、ユーザーの資格情報を検証し、ユーザーがアプリケーションにアクセスできるようにするためのトークンを各アプリケーションに提供する責任があります。次回のアクセスでは、ユーザーは資格情報を再度提供しなくても、トークンを使用して認証できます。

Go 言語では、軽量 Web フレームワーク gin を使用して SSO ログインを実装できます。基本的な SSO アーキテクチャは次のとおりです。

SSO Architecture

上の図では、認証センターと 2 つのアプリケーション (App1 と App2) を定義し、それらの間で認証されます。 SSO ログイン。 JWT (JSON Web Token) は、JSON を使用して情報を記述および送信できる軽量の標準であるため、トークンを表すために使用します。

3.それを達成するにはどうすればよいですか?

  1. GIN のインストール

まず、Gin フレームワークをインストールする必要があります。ターミナルで次のコマンドを入力して、gin をインストールします。

go get -u github.com/gin-gonic/gin
  1. 証明機関の作成

次に、証明機関のコードを記述する必要があります。この例では、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」という 2 つのルートを定義します。

「/login」ルートで、資格情報を読み取って検証し (ここでは単なる例にすぎません)、検証に合格した場合は、JWT トークンを作成してクライアントに送り返します。

「/validate」ルートで、JWT トークンを読み取り、同じ秘密キーを使用して検証します。トークンが有効な場合、トークン内のクレームを抽出してクライアントに送り返します。

  1. アプリケーションの作成

次に、アプリケーションのコードを作成する必要があります。この例では、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」という 2 つのルートを定義しました。

「/login」ルートで、証明機関への GET リクエストを作成し、検証のためにリクエスト ヘッダーに JWT トークンを追加します。検証に合格すると、JWT トークンが作成され、Cookie に保存されます。

「/private」ルートでは、ミドルウェアを使用して、リクエストに JWT トークンが含まれているかどうかを確認します。トークンが有効な場合、トークン内のクレームが抽出され、それに応じて処理されます。

4. まとめ

この記事では、Go 言語を使用してシングル サインオン (SSO) 機能を実装する方法を紹介しました。 Gin フレームワークを使用して HTTP サーバーを作成し、JWT トークンを使用してユーザーを表しました。認証局を定義し、それを使用してリクエストを検証し、トークンを発行します。また、アプリケーションをコーディングし、認証にミドルウェアを使用しました。この例は単なる単純な例であり、特定のアプリケーションのニーズに合わせて拡張できます。

以上がgolang は sso ログインを実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。