简介
1、什么是JWT
JWT
是json web token
缩写。它将用户信息加密
到token
里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token
的正确性,只要正确即通过验证。
2、JWT
结构
JWT
包含三部分:Header(头部)、Payload(负载)、Signature(签名)
最终生成的JWT
样式:base64URLEncoding(Header).
+base64URLEncoding(Payload).
+HMAC-SHA256(base64URLEncoding(Header).+base64URLEncoding(Payload).+密钥)
Header
一般保存两部分信息: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
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