This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.

Introduction to the

Recently, before using Gin to reconstruct the SpringBoot background, although it is popular to use JWT for login authentication, in order to maintain the consistency of the interface, we still choose to use session mechanism for login authentication. The session middleware that is commonly used in Gin is gin-contrib/sessions. The GitHub tutorial provides a simple example of how to package this middleware and obtain information about the current logged-in user.

demand

The previous SpringBoot project can be roughly regarded as a micro Blog platform, with many operations such as updating and deleting articles usually requiring authentication permissions, and only the user can update and delete. The design at that time was to use Seesion to store the user’s basic information (ID, Email, etc.) when the user logged in. When calling the update/delete API, only the parameters related to the article will be passed, and the user parameters will not be passed (the user parameters can be obtained through the sessionId in the header). At that time, Shiro was used to manage sessions, and session data could be obtained by calling Shiro’s SecurityUtils. Since there are many interfaces that may use the information of the current login user, So we write a BaseController to encapsulate the session that reads the user’s information, and then any Controller that needs this function inherits the BaseController.


import org.apache.shiro.SecurityUtils;

public abstract class BaseController {
    /** * Returns information about the current login User. User is a user-defined User class that stores information such as User ID and Email *@return* /
    protected User getCurrentUser(a){
    // Call Shiro's related interface. No context parameters are required
        return (User) SecurityUtils.getSubject().getSession().getAttribute("currentUser");
    }

    /** * Sets the information of the current login user *@param user
     */
    protected void setCurrentUser(User user){
        SecurityUtils.getSubject().getSession().setAttribute("currentUser", user); }}Copy the code

Use sessions in Gin

Gin-contrib /sessions: “secret” is the key used to generate the sessionID. Mysession “mysession” denotes the name of the sessionId returned to the front end. For example, if I fill in” sessionId “, the cookie data returned to the front end will have the sessionId field

package main

import (
  "github.com/gin-contrib/sessions"
  "github.com/gin-contrib/sessions/cookie"
  "github.com/gin-gonic/gin"
)

func main(a) {
  r := gin.Default()
  store := cookie.NewStore([]byte("secret")) // set the key to generate the sessionId
  Mysession is the sessionId name returned to the front end
  r.Use(sessions.Sessions("mysession", store))

  r.GET("/hello".func(c *gin.Context) {
    session := sessions.Default(c)

    if session.Get("hello") != "world" {
      session.Set("hello"."world")
      session.Save()
    }

    c.JSON(200, gin.H{"hello": session.Get("hello")})
  })
  r.Run(": 8000")}Copy the code

Access user information through the Session

There are only simple types of data access described, but sometimes we need to access structure objects in a session, such as the user information I mentioned in the requirements above. Gob is a serialized codec tool that comes with Golang. Gob should be called to Register a custom struct. Otherwise, the following error will be returned when calling ssession. I was stuck here for a long time…

securecookie: error - caused by: securecookie: error - caused by: gob: type not registered for interface: model.User
Copy the code

Golang’s casting (assertion) is also used when reading the User structure using session, because session.Get gets an interface. Then write a simple route, including a login login interface, and write the user information into the session upon successful login. There is also a test interface that returns the username of the currently logged in user without passing parameters. The complete test code is as follows:

type User struct {
	Id       int    `json:"id"`
	Email    string `json:"email"`
	Username string `json:"username"`
	Password string `json:"password"`
}

func getCurrentUser(c *gin.Context) (userInfo User) {
	session := sessions.Default(c)
	userInfo = session.Get("currentUser").(User) // Type conversion
	return
}

func setCurrentUser(c *gin.Context, userInfo User) {
	session := sessions.Default(c)
	session.Set("currentUser", userInfo)
        // Save must be saved otherwise it will not take effect. If the User structure is not registered with gob, the call to Save will return an Error
	session.Save() 
}

func setupRouter(r *gin.Engine) {
	r.POST("/login".func(c *gin.Context) {
		var loginVo User
		ifc.ShouldBindJSON(&loginVo) ! =nil {
			c.String(http.StatusOK, "Parameter error")
			return
		}
		if loginVo.Email == db.Email && loginVo.Password == db.Password {
			setCurrentUser(c, *db) // If the email address and password are correct, the current user information is written to the session
			c.String(http.StatusOK, "Login successful")}else {
			c.String(http.StatusOK, "Login failed")
		}
	})

	r.GET("/sayHello".func(c *gin.Context) {
		userInfo := getCurrentUser(c)
		c.String(http.StatusOK, "Hello "+userInfo.Name)
	})
}

var db = &User{Id: 10001, Email: "[email protected]", Username: "Alice", Password: "123456"} // Do not operate database, write all user information in code

func main(a) {
	gob.Register(User{}) // Register the User structure
	r := gin.Default()
	store := cookie.NewStore([]byte("snaosnca"))
	r.Use(sessions.Sessions("SESSIONID", store))
	setupRouter(r)
	r.Run(": 8080")}Copy the code

A test run

Github go run./main.go then use Postman to send the login request and get the following result:

After a successful login, send a GET request without parameters to the /sayHello interface. The result shown in the following figure shows that session is used to access the information of the current login user.