JWT-golang

JWT简介

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

JWT使用

在认证的时候,即当用户使用凭证成功登录成功后,一个JSON Web Token将会被返回,即token。该字符串即时用户凭证了,当用户想访问受保护的路由或者资源时,用户代理通常是浏览器都应该带上该凭证,通常是放在Authorization Header中。服务器会检查该JWT是否有效,如果有效则才能访问成功。JWT可以携带部分数据以减少数据库压力。

JWT特点

1、无状态。不在服务端存储,减少服务端压力。

2、一次性的。在jwt失效之前都是有效的,信息更新重新签发的新的JWT,旧的还没过期,依旧可以绕过验证。需要服务端有额外的逻辑,防止再次使用。引入redis,其实有点违背特点1了。

3、jwt太长。若在jwt中放入其他数据很可能会出现,http请求Header>Body,因而使用开销略大。

JWT结构

JWT(JSON Web Token)是一个非常轻巧的规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,一个JWT由三部分组成,Header头部、Claims载荷、Signature签名

1、JWT-Header,加密算法通常直接使用SHA256

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

2、JWT-Claims负载

type StandardClaims struct {
    // 受众
	Audience  ClaimStrings `json:"aud,omitempty"`
    // 过期时间
	ExpiresAt *Time        `json:"exp,omitempty"`
    // 编号
	ID        string       `json:"jti,omitempty"`
    // 签发时间
	IssuedAt  *Time        `json:"iat,omitempty"`
    // 签发人
	Issuer    string       `json:"iss,omitempty"`
    // 生效时间
	NotBefore *Time        `json:"nbf,omitempty"`
    // 主题
	Subject   string       `json:"sub,omitempty"`
}

3、JWT-Signature对前两部分进行签名防止被篡改

Token结构体内容:

type Token struct {
	Raw       string                 // The raw token.  Populated when you Parse a token
	Method    SigningMethod          // The signing method used or to be used
	Header    map[string]interface{} // The first segment of the token
	Claims    Claims                 // The second segment of the token
	Signature string                 // The third segment of the token.  Populated when you Parse a token
	Valid     bool                   // Is the token valid?  Populated when you Parse/Verify a token
}

JWT生成流程(github.com/dgrijalva/jwt-go):

1、初始化一个token,可通过构造父类,在Claims中添加一些附加信息

token := jwt.NewWithClaims(
        // 加密方式
		jwt.SigningMethodHS256,
		jwt.StandardClaims{
            // 过期时间
			ExpiresAt: jwt.At(time.Now().Add(xxx)),
		})


// NewWithClaims creats a new token with a specified signing method and claims type
func NewWithClaims(method SigningMethod, claims Claims) *Token {
	return &Token{
		Header: map[string]interface{}{
			"typ": "JWT",
			"alg": method.Alg(),
		},
		Claims: claims,
		Method: method,
	}
}

===>token内容如下:

{
	"token": {
		"Raw": "",
		"Method": {
			"Name": "HS256",
			"Hash": "SHA256"
		},
		"Header": [
			{
				"typ": "JWT",
				"alg": "HS256"
			}
		],
		"StandardClaims": {
			"ExpireAt": "2021-12-24 16:35:14.657703 +0800"
		},
		"Signature": "",
		"Valid": false
	}
}

2、密钥加密生成一个三段式(中间使用两个.隔开)字符串,

tokenString, err := token.SignedString(hmacSecret) //案例如下行:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2luZm8iOnsidWlkIjozLCJyb2xlX2lkIjo1LCJ1c2VybmFtZSI6IirnrqHnkIblkZgiLCJsYXN0X2xvZ2luX2F0IjoiMjAyMS0xMS0yNFQxNjo0ODoxMC44Nzk4OTE0KzA4OjAwIiwidXNlcl9hZ2VudCI6IlBvc3RtYW5SdW50aW1lLzcuMjguNCIsImRldmljZV90eXBlIjoiQU5EUk9JRCIsImVuZF9hdCI6eyJUaW1lIjoiMDAwMS0wMS0wMVQwMDowMDowMFoiLCJWYWxpZCI6ZmFsc2V9fSwiZXhwIjoxNjQwMzM1Njk0Ljk1MjY0N30.lqFu8xEvJMq31wpTVwBYjFRLt8xefHf7OxC1q1oy__8

该字符串包含三个部分,

        Header头部,

        Payload负载即上述Claims

        Signature 签名/签证

// SignedString returns the complete, signed token
func (t *Token) SignedString(key interface{}, opts ...SigningOption) (string, error) {
	var sig, sstr string
	var err error
    /* 
    此方法将token中的Header和Claims分别编码并用点连接,部分源码如下
    func (t *Token) SigningString(opts ...SigningOption) (string, error) {
	    ...
	    inputParts := []interface{}{t.Header, t.Claims}
	    parts := make([]string, 2)
	    for i, v := range inputParts {
		    ctx := CodingContext{FieldDescriptor(i), t.Header}
		    ...
	    }
	    return strings.Join(parts, "."), nil
    }

    */
	if sstr, err = t.SigningString(opts...); err != nil {
		return "", err
	}
    // 对前两部分由.拼接的字符串进行签名
	if sig, err = t.Method.Sign(sstr, key); err != nil {
		return "", err
	}
    // 再拼接.
	return strings.Join([]string{sstr, sig}, "."), nil
}
服务端解码,直接上代码:
func Parse(tokenString string) (*UserInfo, error) {
	token, err := jwt.ParseWithClaims(
		tokenString,
		&UserClaims{},
		func(token *jwt.Token) (interface{}, error) {
			if token.Method.(*jwt.SigningMethodHMAC) != alg {
				return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
			}
			return hmacSecret, nil
		},
	)

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*UserClaims); ok && token.Valid {
		return &claims.UserInfo, nil
	} else {
		return nil, fmt.Errorf("token非法: %s", tokenString)
	}
}

你可能感兴趣的:(jwt,golang)