In Web application development, a Session is a nonpersistent exchange of information between the user and the server. When a user logs in, a Session can be generated between the user and the server, then data can be exchanged back and forth, and the Session can be destroyed when the user logs out. The Gorilla/Sessions package provides an easy-to-use Session implementation for the Go language. The package provides two different implementations. The first is file system storage, which stores each session in the server’s file system. The other is Cookie storage, which uses the SecureCookie described in our previous article to store sessions on the client side. At the same time, it also provides users with the option to customize the implementation of Session storage. We can implement Session storage according to the requirements of the application. Since this tutorial is designed for learning how to use the MySQL or Redis version of Session storage, we will use the Cookie provided by the software package to complete the Session related content.

The source code for each article in the Go Web Programming series is printed with a corresponding version of the package for your reference. Get the source code of this article by replying to gohttp09

Advantages and disadvantages of using cookies to store user sessions

Managing user sessions using cookies on a client has some advantages over managing user sessions on a server. Client sessions increase the scalability of the application because all Session data is stored on the client side, so users’ requests can be balanced to different remote servers and they do not have to be managed uniformly on the server side for all users, so it is easier to store user sessions using cookies.

Of course, every advantage has its disadvantage, and the overall size of the client Cookie is limited. Currently, Google Chrome limits cookies to 4096 bytes.

The client session also means that the session cannot be terminated, resulting in an incomplete logout. If the user saves the session information in the Cookie before exiting, they can use the session information to create a new Cookie and then continue to use the application. In order to minimize security risks, we can set the session Cookie to expire within a reasonable time. Use the encrypted ScureCookie to store data and avoid storing sensitive information (sensitive information such as passwords should not be stored even if the server manages sessions).

In general, it is important to choose between using client or server to store user sessions based on the application scenario.

Install the gorilla/sessions

Install gorilla/ Sessions before you start coding,

$ go get github.com/gorilla/sessions
Copy the code

And a brief look at the software package features

  • Easy to set up signature (optionally encrypted)Cookie.
  • The session is stored in theCookieOr in the server file systemSessionStoreThe implementation.
  • Flash message support: Read and destroy session data.
  • Easily switch the persistence mode of session data.
  • For the differentSessionStorage provides unified interfaces and infrastructure.

Demonstrate user Session design and implementation

Today’s example code implements a simple system login using the CookieSessionStore provided by Gorilla/Sessions.

We will define the following routes:

  • /user/loginUser login authentication, after successful authentication in the userSessionThe marked user in the data is authenticated.
  • /user/logoutUser logout, will be inSessionTo mark the user as unauthenticated.
  • /user/secretBy the userSessionCheck whether the user is authenticated. If the user is not authenticated, the system returns403 ForbiddenError.

In order to achieve the purpose of demonstration and reduce the excessive code in the article, we will not do the front-end page, and directly request the above urls to verify our system login function through the command line cURL.

Initialization work

We will now create a new user subdirectory under the handler directory of the project to hold handlers that use the user Session

. Handler / └ ─ ─ user / └ ─ ─ init. Go └ ─ ─ the login. Go └ ─ ─ logout. Go └ ─ ─ secret. Go... main.goCopy the code

The four following are the package initializer init.go and the. Go source file that holds the three route handlers mentioned above.

Initialize Session storage

We place Session storage initialization in the init function of the User package so that initialization is done when the user package is first imported.

package user

import "github.com/gorilla/sessions"

const (
	/ / 64
	cookieStoreAuthKey = "..."
	//AES Encrypt key must be 16 or 32 bits
	cookieStoreEncryptKey = "..."
)

var sessionStore *sessions.CookieStore

func init (a) {
	sessionStore = sessions.NewCookieStore(
		[]byte(cookieStoreAuthKey),
		[]byte(cookieStoreEncryptKey),
	)

	sessionStore.Options = &sessions.Options{
		HttpOnly: true,
		MaxAge:   60 * 15,}}Copy the code

Implementing login Authentication

// login.go
var sessionCookieName = "user-session"
func Login(w http.ResponseWriter, r *http.Request) {
	session, err := sessionStore.Get(r, sessionCookieName)
	iferr ! =nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	// Login authentication
	name := r.FormValue("name")
	pass := r.FormValue("password")
	_, err = logic.AuthenticateUser(name, pass)
	iferr ! =nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}
	// Mark the user in the session as having passed login authentication
	session.Values["authenticated"] = true
	err = session.Save(r, w)

	fmt.Fprintln(w, "Login successful!", err)
}
Copy the code
  • We’ll put the browserCookieMedium storage userSessiontheCookie-NameSet up auser-session.
  • Login authentication is simply a user name and password lookup that matches the user in the previous articleApplication databaseandApplication of ORMIt’s in two articlesMySQLCreate a databaseusersTable and describes how to use itORMOperate the database. If you haven’t seen it, you can go back and look at it.
  • After login authentication is successful, theSessiontheauthenticatedIndicates that the user has been authenticated.session.ValuesIs a type ofmap[interface{}]interface{}“, so you can store any type of data there.

Implement logout

Logout we simply set Authenticated’s value to false in Session.

//logout.go
func Logout(w http.ResponseWriter, r *http.Request) {
   session, _ := sessionStore.Get(r, sessionCookieName)
   
   session.Values["authenticated"] = false
   session.Save(r, w)
}
Copy the code

Use Session to authenticate users

//secret.go
func Secret(w http.ResponseWriter, r *http.Request) {
   session, _ := sessionStore.Get(r, sessionCookieName)

   if auth, ok := session.Values["authenticated"].(bool); ! ok || ! auth { http.Error(w,"Forbidden", http.StatusForbidden)
      return
   }

   fmt.Fprintln(w, "It's still empty!")}Copy the code
  • useSessionData values stored in the interface are of the interface type, so they must be type asserted before being usedsession.Values["authenticated"].(bool)
  • ifauthenticatedThe value of thetrueOr fromSessionCan not get the corresponding value, return directlyHTTP 403 ForbiddenError.

Registered routing

// router.go
func RegisterRoutes(r *mux.Router) {
  ...
  userRouter := r.PathPrefix("/user").Subrouter()
  userRouter.HandleFunc("/login", user.Login).Methods("POST")
  userRouter.HandleFunc("/secret", user.Secret)
  userRouter.HandleFunc("/logout", user.Logout)
  ...
}
Copy the code

Verify that Session management functions have been implemented

CURL cURL cURL cURL cURL cURL cURL cURL cURL cURL cURL cURL

curl -XPOST   -d 'name=Klein&password=123' \
     -c - http://localhost:8000/user/login
Copy the code

The -c option indicates that the Cookie is written to the following file. The full format is -c –

. A dash without a file name indicates that the Cookie is written to standard output.

As you can see in the figure below, the user-session in the Cookie stores the encrypted session data

Accessing /user/secret without this Cookie in the request returns an HTTP 403 error

CURL cURL request /user/secret

curl --cookie "user-session=MTU4m..." http://localhost:8000/user/secret
Copy the code

The Cookie’s encrypted value is too long and the text is too small. CURL execution shows that the server responded to our request successfully. You can just use your own Cookie value when you experiment.

You can also use PostMan instead of cURL, but PostMan’s return is not as obvious as cURL’s.

The source code for each article in the Go Web Programming series is printed with a corresponding version of the package for your reference. Get the source code of this article by replying to gohttp09

These reviews

Go Web Programming – How to ensure secure transfer of Cookie data

Go Web Programming – Apply ORM

Go Web Programming – Apply ORM

Quick build Go development environment with Docker in 5 minutes

Learn more about writing HTTP servers with Go