以前写过浅谈OAuth2.0和常用密码算法介绍,这次学习了一下JWT。
原理
令牌分为两类,透明令牌是指根据Token获取不到什么有效信息,必须和授权服务器交互,OAuth2就是透明令牌。还有一种就是自包含令牌,Token里包含了核心信息,怎么知道Token是否伪造的呢?因为Token上同时包含了签名,只要检查方有签名所需要的密钥,就能辨别真伪。
组成
JWT由三部分组成,Header+Claims+Signature,这三部分一般做base64url,通过.合并在一起,如下图所示。
payload中有几个字段,意思为:
- iss:颁发人
- iat:颁发时间
- exp:过期时间
- aud:颁发给谁的
- sub:主体
- role:登录用户的信息
通过verify signature字段,能够看出签名是怎么计算出来的。要检验,必须有签名的“secret”。这么看是不是和支票有点相似。
JWT可以通过jwt.io/进行解析验证。
使用
一般客户应用从授权服务器获取JWT后,可以直接请求对应的资源服务器,这些资源服务器会查看JWT的信息,并做签名验证,为什么可以做签名验证?因为他们有授权服务器签名的“secret”。
实战
创建和验证Token一般是分开的,但是为了方便理解,写到一起了。代码位置为:github.com/shidawuhen/…
package oauth
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"net/http"
"time"
)
func JWT(c *gin.Context) {
mySigningKey := []byte("AllYourBase")
tokenString := creatJWT(mySigningKey)
checkJWT(tokenString, mySigningKey)
c.String(http.StatusOK, "ok")
}
func checkJWT(tokenString string, mySigningKey []byte) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
})
switch {
case token.Valid:
fmt.Printf("正确的token %+v", token)
case errors.Is(err, jwt.ErrTokenMalformed):
fmt.Println("That's not even a token")
case errors.Is(err, jwt.ErrTokenSignatureInvalid):
// Invalid signature
fmt.Println("Invalid signature")
case errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet):
// Token is either expired or not active yet
fmt.Println("Timing is everything")
default:
fmt.Println("Couldn't handle this token:", err)
}
}
func creatJWT(mySigningKey []byte) string {
type MyCustomClaims struct {
Foo string `json:"foo"`
jwt.RegisteredClaims
}
// Create claims with multiple fields populated
claims := MyCustomClaims{
"hello",
jwt.RegisteredClaims{
// A usual scenario is to set the expiration time relative to the current time
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: "asap",
Subject: "com",
ID: "1",
Audience: []string{"pzq"},
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(mySigningKey)
fmt.Println(ss, err)
return ss
}
执行效果为:
使用jwt.io验证,如果secret不对
如果secret正确
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:shidawuhen.github.io/
往期文章回顾: