舫摘

知人者智 自知者明 胜人者有力 自胜者强

0%

Securing Golang RESTful APIs with JSON Web Tokens

The official definition of JSON Web Tokens

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely
transmitting information between parties as a JSON object. This information can be verified and trusted because
it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.

Basically, JWT is an encoded string which is safe to send between services and clients. The token represents a value that is accessible only by the service that has access to the secret key with which it was encrypted.

What does this look like in real life?

Let’s say a user logged in their account. The server creates a token using the identification and a secret key. This string of characters that result from the encryption is called a token. Then the server sends it back to the client. The client, in turn, saves the token to use it in every other request the user will send. The practice of adding a token to the request headers is a way of authorizing the user to access resources.

Before the start, Run below command for install all required packages.

1
2
$ go get github.com/dgrijalva/jwt-go
$ go get gopkg.in/mgo.v2

Generate JSON Web Tokens

Create a file main.go to creates an Identification and generates JSON Web Tokens for it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

import (
"fmt"
"time"

jwt "github.com/dgrijalva/jwt-go"
"gopkg.in/mgo.v2/bson"
)

const secretKey string = "DON'T SHARE WITH ANYONE ELSE"

func generateJWT(id string) (string, error) {
duration, err := time.ParseDuration("30m")
if err != nil {
return "", err
}

// the token expires after 30 minutes
expiration := time.Now().Add(duration).Unix()

// change it to your secret key
secret := []byte(secretKey)
claims := struct {
ID string `json:"id"`
jwt.StandardClaims
}{
id,
jwt.StandardClaims{
ExpiresAt: expiration,
},
}

// generate JSON Web Tokens
token := jwt.NewWithClaims(jwt.SigningMethodHS256, &claims)

// transform token to encrypted string
tokenString, err := token.SignedString(secret)
if err != nil {
return "", err
}

return tokenString, nil
}

func main() {
// the identification that we want encryption
id := bson.NewObjectId().Hex()
fmt.Printf("Your Identification: %s\n", id)

tokenString, err := generateJWT(id)
if err == nil {
// print token string on screen
fmt.Printf("Your JSON Web Tokens: %s\n", tokenString)
}
}

then run your program:

1
2
3
$ go run main.go
Your Identification: 5af106d96bf2dfde11756c3a
Your JSON Web Tokens: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVhZjEwNmQ5NmJmMmRmZGUxMTc1NmMzYSIsImV4cCI6MTUyNTc0NzE2OX0.ShMufUD-byPpJlzEoZHOWeSVu2qTf5NxtBZP-pQ4h44

Great! It’s work! We have a new Identification and a JSON Web Tokens string.

Parse JSON Web Tokens

Let’s update main.go add a method for parse JSON Web Tokens string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package main

import (
"fmt"
"time"

jwt "github.com/dgrijalva/jwt-go"
"gopkg.in/mgo.v2/bson"
)

// change it to your secret key in real life
const secretKey string = "DON'T SHARE WITH ANYONE ELSE"

func generateJWT(id string) (string, error) {
duration, err := time.ParseDuration("30m")
if err != nil {
return "", err
}

// the token expires after 30 minutes
expiration := time.Now().Add(duration).Unix()

secret := []byte(secretKey)

claims := struct {
ID string `json:"id"`
jwt.StandardClaims
}{
id,
jwt.StandardClaims{
ExpiresAt: expiration,
},
}

// generate JSON Web Tokens
token := jwt.NewWithClaims(jwt.SigningMethodHS256, &claims)

// transform token to encrypted string
tokenString, err := token.SignedString(secret)
if err != nil {
return "", err
}

return tokenString, nil
}

func parseJWT(tokenString string) (string, error) {
claims := struct {
ID string `json:"id"`
jwt.StandardClaims
}{}

// parse the encrypted string to JSON Web Tokens
token, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
return []byte(secretKey), nil
})

if err == nil && token.Valid {
return claims.ID, nil
} else {
return "", nil
}
}

func main() {
// the in identification that we want encryption
id := bson.NewObjectId().Hex()
fmt.Printf("Your Identification: %s\n", id)

tokenString, err := generateJWT(id)
if err == nil {
// print token string on screen
fmt.Printf("Your JSON Web Tokens: %s\n", tokenString)
}

id, err = parseJWT(tokenString)
if err == nil {
// print token string on screen
fmt.Printf("Your Secret: %s\n", id)
}
}

then run your program again:

1
2
3
4
$ go run main.go
Your Identification: 5af1072a6bf2dfde366113b6
Your JSON Web Tokens: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVhZjEwNzJhNmJmMmRmZGUzNjYxMTNiNiIsImV4cCI6MTUyNTc0NzI1MH0.SI0WtKg06yXWBSQHi_LzWPU2SYqDOFcduDKmNLXkcSg
Your Secret: 5af1072a6bf2dfde366113b6

The program creates an Identification then generates a JSON Web Tokens string for it. At last, reverse JSON Web Tokens string to Identification.

We can see the result from the decryption exactly the same.

Hope you guys and girls enjoyed reading this as much as I enjoyed writing it. Have fun.