jwt 初体验

简介

1、什么是JWT

JWTjson web token缩写。它将用户信息加密 到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。

2、JWT结构

JWT包含三部分:Header(头部)、Payload(负载)、Signature(签名)

最终生成的JWT样式:base64URLEncoding(Header).+base64URLEncoding(Payload).+HMAC-SHA256(base64URLEncoding(Header).+base64URLEncoding(Payload).+密钥)

一般保存两部分信息:token类型和加密算法

{
    "alg":"HS256",
    "typ":"JWT"
}

经过Base64Url编码后作为JWT结构的第一部分。

Payload

Token的第二部分是负载,它包含了claim, Claim是一些实体(通常指的用户)的状态和额外的元数据,有三种类型的claim:reserved, public 和 private.Reserved claims: 这些claim是JWT预先定义的,在JWT中并不会强制使用它们,而是推荐使用,常用的有 iss(签发者),exp(过期时间戳), sub(面向的用户), aud(接收方), iat(签发时间)。 Public claims:根据需要定义自己的字段,注意应该避免冲突 Private claims:这些是自定义的字段,可以用来在双方之间交换信息

{ 
    "sub": "1234567890", 
    "name": "John Doe",
    "admin": true
 }
 

经过Base64Url编码后作为JWT结构的第二部分。

Signature

创建签名需要使用编码后的header和payload以及一个秘钥,使用header中指定签名算法进行签名。例如如果希望使用HMAC SHA256算法,那么签名应该使用下列方式创建: HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) 签名用于验证消息的发送者以及消息是没有经过篡改的。 完整的JWT 完整的JWT格式的输出是以. 分隔的三段Base64编码,与SAML等基于XML的标准相比,JWT在HTTP和HTML环境中更容易传递。 下列的JWT展示了一个完整的JWT格式,它拼接了之前的Header, Payload以及秘钥签名:

<font color=red>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.</font><font color=purple>eyJleHAiOjE1MzU5NDgyNzMsImlhdCI6MTUzNTk0NDY3MywidXNlcm5hbWUiOiJzd2VldHNtZWxvbiJ9.</font><font color=blue>cfyaGsbVrHalZik1HPPqZAttk3sjM7XY-mBhdmCSis8</font>

如何使用JWT

使用场景:身份鉴定、信息交换

以身份鉴定为例:

在身份鉴定的实现中,传统方法是在服务端存储一个session,给客户端返回一个cookie,而使用JWT之后,当用户使用它的认证信息登陆系统之后,会返回给用户一个JWT,用户只需要本地保存该token(通常使用local storage,也可以使用cookie)即可。 当用户希望访问一个受保护的路由或者资源的时候,通常应该在Authorization头部使用Bearer模式添加JWT,其内容看起来是下面这样:Authorization: Bearer 因为用户的状态在服务端的内存中是不存储的,所以这是一种无状态的认证机制。服务端的保护路由将会检查请求头Authorization中的JWT信息,如果合法,则允许用户的行为。由于JWT是自包含的,因此减少了需要查询数据库的需要。 JWT的这些特性使得我们可以完全依赖其无状态的特性提供数据API服务,甚至是创建一个下载流服务。因为JWT并不使用Cookie的,所以你可以使用任何域名提供你的API服务而不需要担心跨域资源共享问题(CORS)。 下面的序列图展示了该过程:

go 实现myJWT

package main

import (
	"time"
	"encoding/json"
	"encoding/base64"
	"fmt"
	"crypto/hmac"
	"crypto/sha256"
	"strings"
)

const kScreateKey  = "123"
type Header struct {
	//加密方式
	Alg string
	//加密类型
	Typ string
}
type Payload struct {
	//过期时间
	Exp int64
	//签发时间
	Iat int64
	//用户名
	UserName string
}

type jwt struct {
	Header Header
	Payload Payload
	Signature string
}

func main() {
	token := createToken()
	//token := "eyJBbGciOiJIUzI1NiIsIlR5cCI6IkpXVCJ9.eyJFeHAiOjE1MzU5NDk5MjUsIklhdCI6MTUzNTk0OTkyMywiVXNlck5hbWUiOiJzd2VldHNtZWxvbiJ9.C6XqHbR3Td9PhgTofVGlLnQ6PAab2pHm9T7yj_NtoQs"
	verifyToken(token)
}
//创建token
func createToken() string {

	//1、创建header
	header := Header{"HS256","JWT"}
	//2、创建JWT第一段 header的base64编码
	headerBytes,err := json.Marshal(header)
	fmt.Println(err)
	header_Base64 := base64.URLEncoding.EncodeToString(headerBytes)
	//3、创建payload
	payload := Payload{}
	//token两秒后过期
	payload.Exp = time.Now().Add(time.Second*time.Duration(2)).Unix()
	payload.Iat = time.Now().Unix()
	payload.UserName = "sweetsmelon"
	//4、创建JWT第二段 payload的base64编码
	payloadBytes,_:= json.Marshal(payload)
	payload_Base64 := base64.URLEncoding.EncodeToString(payloadBytes)

	//5、拼接header和payload
	hpString := fmt.Sprintf("%s.%s",header_Base64,payload_Base64)
	//6、生成签名
	signatureStr := createSianature(hpString)
	token := fmt.Sprintf("%s.%s",hpString,signatureStr)
	fmt.Println(token)

	return token
}
func createSianature(hpString string) string  {
	hasher := hmac.New(sha256.New,[]byte(kScreateKey))
	hasher.Write([]byte(hpString))
	signatureStr :=strings.TrimRight(base64.URLEncoding.EncodeToString(hasher.Sum(nil)), "=")
	return signatureStr
}
func verifyToken(token string)  {
	//1、根据"."拆分token
	jwt := strings.Split(token,".")
	//2、解析header
	headerStr := jwt[0]
	headerBytes,_ := base64.URLEncoding.DecodeString(headerStr)
	header := Header{}
	json.Unmarshal(headerBytes,&header)
	//3、解析payload
	payloadStr := jwt[1]
	payloadBytes,_ := base64.URLEncoding.DecodeString(payloadStr)
	payload := Payload{}
	json.Unmarshal(payloadBytes,&payload)
	//4、验证签名是否有效
	hpString := fmt.Sprintf("%s.%s",headerStr,payloadStr)
	signatureString := createSianature(hpString)
	if signatureString == jwt[2] {
		if header.Alg == "HS256" && header.Typ == "JWT" {
			//3、判断是否过期
			if payload.Exp < time.Now().Unix() {
				fmt.Println("验证码已失效")
			}else {
				fmt.Println("验证成功 username=",payload.UserName)
			}
		}else {
			fmt.Println("验证header失败")
		}
	}else {
		fmt.Println("验证签名失败")
	}
}

使用 jwt-go

jwt-go是一个封装好的go语言版的jwt工具

安装jwt-go

go get github.com/dgrijalva/jwt-go

使用

package main

import (
	"time"
	"github.com/dgrijalva/jwt-go"
	"reflect"
	"fmt"
)
const kSecretKey = "123456"
func main() {
	createToken()
}
func createToken() {
	token := jwt.New(jwt.SigningMethodHS256)
	claims := make(jwt.MapClaims)
	claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
	claims["iat"] = time.Now().Unix()
	//自定义属性username
	claims["username"] = "sweetsmelon"
	token.Claims = claims

	tokenString, err := token.SignedString([]byte(kSecretKey))
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(tokenString)
	veiftyToken(tokenString)
}
func veiftyToken(tokenStr string)  {
	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		return []byte(kSecretKey), nil
	})
	//通过映射,获取自定义的Claims属性
	switch token.Claims.(type) {
	case jwt.MapClaims:
		{
			v := reflect.ValueOf(token.Claims)
			v2 := v.Interface().(jwt.MapClaims)
			fmt.Println(v2["username"])
			if err != nil {
				fmt.Println("验证失败")
			} else {
				if token.Valid {
					fmt.Println("验证成功")
				} else {
					fmt.Println("验证码失效")
				}
			}
			return
		}
	}
	fmt.Println("token格式错误,验证失败")
}

参考资料:

https://jwt.io/introduction/

https://blog.csdn.net/qq_40081976/article/details/79046825

http://blog.51cto.com/xwandrew/2050973

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦