The authentication

After the first login, a token with a certain validity period is generated and stored in the Cookie or LocalStorage of the browser. Subsequent requests carry the token. After the request arrives at the server, the server uses the token to authenticate the request.

A simpler approach is to symmetrically encrypt user information and time stamps directly with the key, which saves extra storage and reduces the query pressure on the database with each request. In this way, there is a standard implementation in the industry, which is called JSON Web Token

JWT

In JWT, the Token consists of three parts, separated by a., and encoded in Base64:

  • header

  • payload

  • signature

header

The JWT Token header contains two parts of information:

  • The type of the Token
  • Encryption algorithm used by the Token

For example,

{
  "typ": "JWT"."alg": "HS256"
}
Copy the code

payload

To place the token content, you can put some standard fields and some additional fields. Standard fields include:

  • Iss: issuer of JWT Token
  • Sub: theme
  • Exp: indicates the expiration time of the JWT Token
  • Aud: the party receiving the JWT Token
  • Iat: JWT Token issue time
  • NBF: JWT Token validity time
  • Jti: indicates the JWT Token ID

Signature

Generate through the previous two parts. Generation method:

  1. Encode header.payload in Base64
  2. Use Secret (Secret is a password stored on the server) to encrypt the encoded content, and the encrypted content is called Signature

Tokens are generated based on ID and user name

func Sign(ctx *gin.Context, c Context, secret string) (tokenString string, err error) {
	// Load the jwt secret from the Gin config if the secret isn't specified.
	if secret == "" {
		secret = viper.GetString("jwt_secret")
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"id":       c.ID,
		"username": c.Username,
		"nbf":      time.Now().Unix(),
		"iat":      time.Now().Unix(),
	})
	tokenString, err = token.SignedString([]byte(secret))
	return
}
Copy the code

Extract the token from the header of the request

func ParseRequest(c *gin.Context) (*Context, error) {
	header := c.Request.Header.Get("Authorization")

	secret := viper.GetString("jwt_secret")

	if len(header) == 0 {
		return &Context{}, ErrMissingHeader
	}

	var t string
	fmt.Sscanf(header, "Bearer %s", &t)
	return Parse(t, secret)
}
Copy the code

Check the format of secret

func secretFunc(secret string) jwt.Keyfunc {
	return func(token *jwt.Token) (interface{}, error) {
		// Make sure the `alg` is what we except.
		if_, ok := token.Method.(*jwt.SigningMethodHMAC); ! ok {return nil, jwt.ErrSignatureInvalid
		}
		return []byte(secret), nil}}Copy the code

Parsing the token

func Parse(tokenString string, secret string) (*Context, error) {
	ctx := &Context{}
	token, err := jwt.Parse(tokenString, secretFunc(secret))
	iferr ! =nil {
		return ctx, err
	} else if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		ctx.ID = uint64(claims["id"]. (float64))
		ctx.Username = claims["username"]. (string)
		return ctx, nil
	} else {
		return ctx, err
	}
}
Copy the code

test

The user login

Access without token

Token Access