golang 在go中使用JWT进行http身份验证的示例

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang 在go中使用JWT进行http身份验证的示例相关的知识,希望对你有一定的参考价值。

package main

// using asymmetric crypto/RSA keys

import (
	"crypto/rsa"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
)

// location of the files used for signing and verification
const (
	privKeyPath = "keys/app.rsa"     // openssl genrsa -out app.rsa keysize
	pubKeyPath  = "keys/app.rsa.pub" // openssl rsa -in app.rsa -pubout > app.rsa.pub
)

// keys are held in global variables
// i havn't seen a memory corruption/info leakage in go yet
// but maybe it's a better idea, just to store the public key in ram?
// and load the signKey on every signing request? depends on  your usage i guess
var (
	verifyKey *rsa.PublicKey
	signKey   *rsa.PrivateKey
)

// read the key files before starting http handlers
func init() {
	signBytes, err := ioutil.ReadFile(privKeyPath)
	fatal(err)

	signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
	fatal(err)

	verifyBytes, err := ioutil.ReadFile(pubKeyPath)
	fatal(err)

	verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
	fatal(err)
}

func fatal(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// just some html, to lazy for http.FileServer()
const (
	tokenName = "AccessToken"

	landingHtml = `<h2>Welcome to the JWT Test</h2>

<a href="/restricted">fun area</a>

<form action="/authenticate" method="POST">
	<input type="text" name="user">
	<input type="password" name="pass">
	<input type="submit">
</form>`

	successHtml    = `<h2>Token Set - have fun!</h2><p>Go <a href="/">Back...</a></p>`
	restrictedHtml = `<h1>Welcome!!</h1><img src="https://httpcats.herokuapp.com/200" alt="" />`
)

// serves the form and restricted link
func landingHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, landingHtml)
}

// reads the form values, checks them and creates the token
func authHandler(w http.ResponseWriter, r *http.Request) {
	// make sure its post
	if r.Method != "POST" {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, "No POST", r.Method)
		return
	}

	user := r.FormValue("user")
	pass := r.FormValue("pass")

	log.Printf("Authenticate: user[%s] pass[%s]\n", user, pass)

	// check values
	if user != "test" || pass != "known" {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprintln(w, "Wrong info")
		return
	}

	// create a signer for rsa 256
	t := jwt.New(jwt.GetSigningMethod("RS256"))

	// set our claims
	t.Claims["AccessToken"] = "level1"
	t.Claims["CustomUserInfo"] = struct {
		Name string
		Kind string
	}{user, "human"}

	// set the expire time
	// see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20#section-4.1.4
	t.Claims["exp"] = time.Now().Add(time.Minute * 1).Unix()
	tokenString, err := t.SignedString(signKey)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Sorry, error while Signing Token!")
		log.Printf("Token Signing error: %v\n", err)
		return
	}

	// i know using cookies to store the token isn't really helpfull for cross domain api usage
	// but it's just an example and i did not want to involve javascript
	http.SetCookie(w, &http.Cookie{
		Name:       tokenName,
		Value:      tokenString,
		Path:       "/",
		RawExpires: "0",
	})

	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, successHtml)
}

// only accessible with a valid token
func restrictedHandler(w http.ResponseWriter, r *http.Request) {
	// check if we have a cookie with out tokenName
	tokenCookie, err := r.Cookie(tokenName)
	switch {
	case err == http.ErrNoCookie:
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "No Token, no fun!")
		return
	case err != nil:
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Error while Parsing cookie!")
		log.Printf("Cookie parse error: %v\n", err)
		return
	}

	// just for the lulz, check if it is empty.. should fail on Parse anyway..
	if tokenCookie.Value == "" {
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "No Token, no fun!")
		return
	}

	// validate the token
	token, err := jwt.Parse(tokenCookie.Value, func(token *jwt.Token) (interface{}, error) {
		// since we only use the one private key to sign the tokens,
		// we also only use its public counter part to verify
		return verifyKey, nil
	})

	// branch out into the possible error from signing
	switch err.(type) {

	case nil: // no error

		if !token.Valid { // but may still be invalid
			w.WriteHeader(http.StatusUnauthorized)
			fmt.Fprintln(w, "WHAT? Invalid Token? F*** off!")
			return
		}

		// see stdout and watch for the CustomUserInfo, nicely unmarshalled
		log.Printf("Someone accessed resricted area! Token:%+v\n", token)
		w.Header().Set("Content-Type", "text/html")
		w.WriteHeader(http.StatusOK)
		fmt.Fprintln(w, restrictedHtml)

	case *jwt.ValidationError: // something was wrong during the validation
		vErr := err.(*jwt.ValidationError)

		switch vErr.Errors {
		case jwt.ValidationErrorExpired:
			w.WriteHeader(http.StatusUnauthorized)
			fmt.Fprintln(w, "Token Expired, get a new one.")
			return

		default:
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, "Error while Parsing Token!")
			log.Printf("ValidationError error: %+v\n", vErr.Errors)
			return
		}

	default: // something else went wrong
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Error while Parsing Token!")
		log.Printf("Token parse error: %v\n", err)
		return
	}

}

// setup the handlers and start listening to requests
func main() {

	http.HandleFunc("/", landingHandler)
	http.HandleFunc("/authenticate", authHandler)
	http.HandleFunc("/restricted", restrictedHandler)

	log.Println("Listening...")
	fatal(http.ListenAndServe(":8080", nil))
}

在 Angular / Golang 项目中使用 JWT

【中文标题】在 Angular / Golang 项目中使用 JWT【英文标题】:Using JWT in a Angular / Golang project 【发布时间】:2016-08-25 21:18:23 【问题描述】:

我在弄清楚如何为我的项目正确使用 JWT 时遇到问题。

情况是这样的:

我有一个网站,人们可以通过 twitch 登录,它会根据我的要求给我一个 oauth 令牌、他们的用户名和更多内容。 (这就是身份验证过程的样子https://github.com/justintv/Twitch-API/blob/master/authentication.md)

在我的数据库中,我有一些具有访问级别的用户名。例如,级别 500 意味着他们可以在我的网站上看到管理仪表板,或者他们可以从 Angular 发送 POST 请求以更改数据。

我的 Angular 应用程序显然无法访问数据库。我只想通过我的 go 网络服务器提供的 API 进行通信。

我想知道的是如何确保尝试发送安全 POST 或 GET 的用户实际上是他所说的用户并且有权执行他正在执行的命令。

【问题讨论】:

【参考方案1】:

JWT 由 3 部分组成:标头(用于“元数据”,如使用的加密算法等)、声明(存储在令牌中的实际数据)和 HMAC(用于验证上面两部分没有被篡改)。

在您的情况下,当您用户登录时,您应该收到 oauth 令牌、他们的用户名等。然后您可以使用用户名来获取关联的用户级别,并将所有内容存储在您自己的 JWT 声明中,您将发回给用户。在随后的请求中,您只需要检查令牌是否有效(不要忘记给他们一个简短的 ttl),然后您就可以确定发出请求的用户是它声称的用户它是(助记符双关语)。

【讨论】:

以上是关于golang 在go中使用JWT进行http身份验证的示例的主要内容,如果未能解决你的问题,请参考以下文章

jwt-go库介绍

我可以将 JWT 令牌放在 Golang Echo 框架的上下文中吗?

Go 和 JWT - 简单的身份验证

JWT

如何在 Golang 中使用摘要身份验证进行 HTTP POST?

[Go] Golang练习项目-web客服系统即时通讯websocket项目go-fly