Home  >  Article  >  Backend Development  >  golang implements sso login

golang implements sso login

PHPz
PHPzOriginal
2023-05-10 11:33:361178browse

With the development of the Internet, more and more websites and applications need to implement single sign-on (SSO) functions to provide better user experience and higher security. In this article, we will introduce how to implement SSO login using Go language.

1. What is SSO?

Single sign-on (SSO) is an authentication protocol that allows users to use one username and password to access multiple applications, rather than authenticating individually for each application. In an SSO system, users only need to provide credentials the first time they log in, and they are automatically logged in on subsequent logins.

The core principle of SSO is to share credentials between different domains. In this case, the user's credentials (such as username and password) only need to be verified in one domain and can then be used to access applications in other domains. This process can simplify the user's login process, reduce the user's burden, and improve security.

2. How to implement SSO?

In order to implement SSO login, we need to define a public authentication center (Authentication Center) that communicates with each application. The authentication authority is responsible for validating the user's credentials and then providing a token to each application to allow the user to access the application. On subsequent visits, the user can use the token to authenticate without providing credentials again.

In Go language, we can use the lightweight web framework gin to implement SSO login. The following is a basic SSO architecture:

SSO Architecture

In the above figure, we define a certification center and two applications (App1 and App2), which are authenticated between them The center implements SSO login. We use JWT (JSON Web Token) to represent tokens because it is a lightweight standard that can use JSON to describe and transmit information.

3. How to achieve it?

  1. Install GIN

First, we need to install the Gin framework. Enter the following command on the terminal to install gin:

go get -u github.com/gin-gonic/gin
  1. Writing the certification authority

Next, we need to write the code for the certification authority. In this example, we will use the Gin framework to create an HTTP server. We need to log the user in on the server and create a JWT token to represent the user. The token will be returned to the user after authentication and will be verified on subsequent requests.

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

In the certification center, we define two routes "/login" and "/validate".

On the "/login" route, we read the credentials and validate them (just a simple example here), and if the validation passes, we create a JWT token and send it back to the client.

On the "/validate" route, we read the JWT token and validate it using the same secret key. If the token is valid, we extract the claims within it and send them back to the client.

  1. Writing the Application

Next, we need to write the code for the application. In this example, we will use the Gin framework to create an HTTP server. We need to access the authentication authority to verify the user's credentials and obtain the JWT token after verification. The token will be stored in a cookie and verified on subsequent requests.

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

In the application, we have defined two routes "/login" and "/private".

On the "/login" route, we make a GET request to the certification authority and add the JWT token in the request header for verification. If the verification passes, we create a JWT token and store it in a cookie.

On the "/private" route, we use middleware to verify whether the JWT token is included in the request. If the token is valid, we will extract the claims within it and process them accordingly.

4. Summary

In this article, we introduced how to use the Go language to implement the single sign-on (SSO) function. We used the Gin framework to create the HTTP server and used JWT tokens to represent users. We define a certification authority and use it to validate requests and issue tokens. We also coded the application and used middleware for authentication. This example is just a simple example and can be extended to meet specific application needs.

The above is the detailed content of golang implements sso login. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn