>  기사  >  백엔드 개발  >  golang은 SSO 로그인을 구현합니다.

golang은 SSO 로그인을 구현합니다.

PHPz
PHPz원래의
2023-05-10 11:33:361178검색

인터넷이 발전함에 따라 더 나은 사용자 경험과 더 높은 보안을 제공하기 위해 SSO(Single Sign-On) 기능을 구현해야 하는 웹사이트와 애플리케이션이 점점 더 많아지고 있습니다. 이번 글에서는 Go 언어를 이용하여 SSO 로그인을 구현하는 방법을 소개하겠습니다.

1. SSO란 무엇인가요?

SSO(Single Sign-On)는 사용자가 각 애플리케이션에 대해 개별적으로 인증하는 대신 하나의 사용자 이름과 비밀번호를 사용하여 여러 애플리케이션에 액세스할 수 있도록 하는 인증 프로토콜입니다. SSO 시스템에서 사용자는 처음 로그인할 때만 자격 증명을 제공하면 되며 이후 로그인 시 자동으로 로그인됩니다.

SSO의 핵심 원칙은 서로 다른 도메인 간에 자격 증명을 공유하는 것입니다. 이 경우 사용자의 자격 증명(예: 사용자 이름 및 비밀번호)은 한 도메인에서만 확인하면 되며 다른 도메인의 애플리케이션에 액세스하는 데 사용할 수 있습니다. 이 프로세스는 사용자의 로그인 프로세스를 단순화하고 사용자의 부담을 줄이며 보안을 향상시킬 수 있습니다.

2. SSO를 구현하는 방법은 무엇입니까?

SSO 로그인을 구현하려면 각 애플리케이션과 통신하는 공용 인증 센터(Authentication Center)를 정의해야 합니다. 인증 기관은 사용자의 자격 증명을 확인한 다음 사용자가 애플리케이션에 액세스할 수 있도록 각 애플리케이션에 토큰을 제공하는 일을 담당합니다. 이후 방문 시 사용자는 자격 증명을 다시 제공하지 않고도 토큰을 사용하여 인증할 수 있습니다.

Go 언어에서는 경량 웹 프레임워크 gin을 사용하여 SSO 로그인을 구현할 수 있습니다. 다음은 기본 SSO 아키텍처입니다.

SSO Architecture

위 그림에서는 인증 센터와 인증 센터를 통해 SSO 로그인을 구현하는 두 개의 애플리케이션(App1 및 App2)을 정의합니다. JSON을 사용하여 정보를 설명하고 전송할 수 있는 경량 표준이기 때문에 JWT(JSON Web Token)를 사용하여 토큰을 나타냅니다.

3. 그것을 달성하는 방법은 무엇입니까?

  1. GIN 설치

먼저 Gin 프레임워크를 설치해야 합니다. gin을 설치하려면 터미널에 다음 명령을 입력하세요.

go get -u github.com/gin-gonic/gin
  1. Writing the Certification Center

다음으로, 인증 센터에 대한 코드를 작성해야 합니다. 이 예에서는 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 토큰을 읽고 동일한 비밀 키를 사용하여 유효성을 검사합니다. 토큰이 유효하면 그 안에 있는 클레임을 추출하여 클라이언트에 다시 보냅니다.

  1. Writing the Application

다음으로, 애플리케이션의 코드를 작성해야 합니다. 이 예에서는 Gin 프레임워크를 사용하여 HTTP 서버를 생성합니다. 사용자의 자격 증명을 확인하고 확인 후 JWT 토큰을 얻기 위해 인증 기관에 접근해야 합니다. 토큰은 쿠키에 저장되며 후속 요청 시 확인됩니다.

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 토큰을 생성하여 쿠키에 저장합니다.

"/private" 경로에서는 미들웨어를 사용하여 JWT 토큰이 요청에 포함되어 있는지 확인합니다. 토큰이 유효하면 그 안에 있는 클레임을 추출하여 그에 따라 처리합니다.

4. 요약

이번 글에서는 Go 언어를 사용하여 SSO(Single Sign-On) 기능을 구현하는 방법을 소개했습니다. 우리는 Gin 프레임워크를 사용하여 HTTP 서버를 생성하고 JWT 토큰을 사용하여 사용자를 나타냈습니다. 인증 기관을 정의하고 이를 사용하여 요청을 검증하고 토큰을 발행합니다. 또한 애플리케이션을 코딩하고 인증을 위해 미들웨어를 사용했습니다. 이 예는 단순한 예일 뿐이며 특정 애플리케이션 요구 사항을 충족하도록 확장될 수 있습니다.

위 내용은 golang은 SSO 로그인을 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.