Skip to main content
 首页 » 编程设计

使用JWT保护Golang Rest API

2022年07月19日139傻小

JWT(JSON Web Token) 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC7519)。JWT允许以无状态但安全的方式将信息从客户机传输到服务器,它特别适用于分布式站点的单点登录(SSO)场景。
前文介绍JWT实现机制,本文通过示例讲解JWT的一种应用场景。

JWT介绍

JWT标准使用加密算法要么使用使用HMAC算法,要么使用RSA或ECDSA的公钥/私钥对非对称加密算法。它在单页应用程序(SPA)中大量使用,作为安全通信的手段,因为它主要实现两个关键事项:

  • 身份认证

这是最常用的用法。一旦用户登录到应用程序、或者以某种方式进行了身份验证,那么代表用户从客户端发送的每个请求都将包含JWT。

  • 信息交换

JWT的第二个用途是在不同的系统之间安全地传输信息。这些JWT可以使用公钥/私钥对签名,能够以安全的方式验证该事务中的每个系统,并且JWT包含一个反篡改机制,因为它是基于头和负载签名的。

本文将详细介绍如何构建一个使用JSON Web token进行Golang的安全REST API通信!

JWT示例

我们打算使用Golang实现一个简单版本Rest方式的Hello World WEB应用。

代码如下:

package main 
 
import ( 
    "fmt" 
    "log" 
    "net/http" 
) 
 
func homePage(w http.ResponseWriter, r *http.Request){ 
    fmt.Fprintf(w, "Hello World") 
    fmt.Println("Endpoint Hit: homePage") 
} 
 
func handleRequests() { 
    http.HandleFunc("/", homePage) 
    log.Fatal(http.ListenAndServe(":8081", nil)) 
} 
 
func main() { 
    handleRequests() 
} 

当我们访问 http://localhost:8081/ 时,可以在浏览器中看到 Hello World.

JWT认证

上面已经搭建了一个简单API接口,我们现在要使用JWT token进行保护。需要新建客户端API尝试请求Hello World
接口。我们对密钥进行签名生成JWT,使得服务端与客户端都不会看到:

  1. 客户端基于共享口令生成签名JWT
  2. 客户端再次访问服务端API,并把JWT作为请求的一部分
  3. 服务端读取请求JWT,并使用相同口令验证token
  4. 如果JWT有效,会返回受保护的Hello World消息,否则会返回未认证

服务端实现

下面实现服务端:

 
package main 
 
import ( 
	"fmt" 
	"log" 
	"net/http" 
 
	jwt "github.com/dgrijalva/jwt-go" 
) 
 
var mySigningKey = []byte("captainjacksparrowsayshi") 
 
func homePage(w http.ResponseWriter, r *http.Request) { 
	fmt.Fprintf(w, "Hello World") 
	fmt.Println("Endpoint Hit: homePage") 
 
} 
 
func isAuthorized(endpoint func(http.ResponseWriter, *http.Request)) http.Handler { 
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
 
		if r.Header["Token"] != nil { 
 
			token, err := jwt.Parse(r.Header["Token"][0], func(token *jwt.Token) (interface{}, error) { 
				if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 
					return nil, fmt.Errorf("There was an error") 
				} 
				return mySigningKey, nil 
			}) 
 
			if err != nil { 
				fmt.Fprintf(w, err.Error()) 
			} 
 
			if token.Valid { 
				endpoint(w, r) 
			} 
		} else { 
			fmt.Fprintf(w, "Not Authorized") 
		} 
	}) 
} 
 
func handleRequests() { 
	http.Handle("/", isAuthorized(homePage)) 
	log.Fatal(http.ListenAndServe(":9000", nil)) 
} 
 
func main() { 
	handleRequests() 
} 
 

现在我们解释上述代码。我们创建了非常简单的API,然后使用isAuthorized装饰中间件函数进行保护,在函数内检查请求头中是否有Token,如果有则进一步检查是否有效。如果有效则可以正常访问该地址,反之提示未授权。

客户端实现

完成了服务端API保护之后,下面编写客户端与之交互。下面代码定义一个简单客户端应用,尝试访问服务端/地址.

package main 
 
import ( 
	"fmt" 
	"io/ioutil" 
	"log" 
	"net/http" 
	"time" 
 
	jwt "github.com/dgrijalva/jwt-go" 
) 
 
var mySigningKey = []byte("captainjacksparrowsayshi") 
 
func homePage(w http.ResponseWriter, r *http.Request) { 
	validToken, err := GenerateJWT() 
	if err != nil { 
		fmt.Println("Failed to generate token") 
	} 
 
	client := &http.Client{} 
	req, _ := http.NewRequest("GET", "http://localhost:9000/", nil) 
	req.Header.Set("Token", validToken) 
	res, err := client.Do(req) 
	if err != nil { 
		fmt.Fprintf(w, "Error: %s", err.Error()) 
	} 
 
	body, err := ioutil.ReadAll(res.Body) 
	if err != nil { 
		fmt.Println(err) 
	} 
	fmt.Fprintf(w, string(body)) 
} 
 
func GenerateJWT() (string, error) { 
	token := jwt.New(jwt.SigningMethodHS256) 
 
	claims := token.Claims.(jwt.MapClaims) 
 
	claims["authorized"] = true 
	claims["client"] = "Elliot Forbes" 
	claims["exp"] = time.Now().Add(time.Minute * 30).Unix() 
 
	tokenString, err := token.SignedString(mySigningKey) 
 
	if err != nil { 
		fmt.Errorf("Something Went Wrong: %s", err.Error()) 
		return "", err 
	} 
 
	return tokenString, nil 
} 
 
func handleRequests() { 
	http.HandleFunc("/", homePage) 
 
	log.Fatal(http.ListenAndServe(":8001", nil)) 
} 
 
func main() { 
	handleRequests() 
} 

我们简要解释下上述代码。首先定义简单API入口,访问会触使用mySigningKey生成新的JWT,然后创建http client,并设置Token头信息为生成的JWT字符串。

首先运行服务端程序,使用浏览器访问http://localhost:9000,会返回Not Authorized,因为我们没有在请求头中设置token。然后我们运行客户端程序,访问http://localhost:8001,因为我们在代码中设置了token,因此可以正常访问并显示Hello World信息。

总结

本文介绍JWT的概念,并通过示例展示请求与响应过程中JWT的作用。参考内容:https://tutorialedge.net/golang/authenticating-golang-rest-api-with-jwts/


本文参考链接:https://blog.csdn.net/neweastsun/article/details/123192282
阅读延展