SDU信息门户(14)项目的Jwt代码解析

2021SC@SDUSC

目录

一.引言

二.代码分析

1.基本概念

2.生成jwtToken

3.解析校验Token

三.总结


一.引言

        我们使用Jwt来保卫我们的系统。因为在SDU信息门户项目中,我们有三种角色:管理员、教师、学生。如果其他用户知道了项目的管理员接口,可能导致软件的失败。如果仅仅是通过调用的接口路径的不同来区分这些角色的话,还是无法完全避免软件缺陷。

二.代码分析

1.基本概念

JWT 由三部分构成,第一部分是 header,第二部分为 payload,第三部分是 signature。

在 header 中存放着令牌类型和令牌使用的加密算法。

在 payload 存放有效信息,这些有效信息包含三个部分:标准中注册的声明、共有的声明和私有的声明。jwt.StandardClaims定义的就是标准中注册的声明。

在signature存放签证信息,用于校验消息在整个过程中有没有被篡改。

jwt.SandardClaims结构体中,Audience是受众,即接受 JWT 的一方,ExpiresAt是所签发的 JWT 过期时间,Id是 JWT 的唯一标识,IssueAt是签发时间,Issuer是 JWT 的签发者,NotBefore是 JWT 的生效时间,Subject是主题。

2.生成jwtToken

GenerateToken方法用于生成 JWT Token,它利用参数中传入的appKeyappSecret,以及配置文件中的Issuer(签发者)、Expire(有效时间),根据指定的算法生成签名后的 Token。time.Now可以获取当前时间,用这个时间加上 Token 的有效时间Expire,得到过期时间expireTime,再利用Unix方法,得到一个int64类型的、从时间点 January 1, 1970 UTC 到时间点t所经过的时间(单位 s)。参数中的appKeyappSecret并没有直接传入Claims结构体,而是经过了 MD5 加密。NewWithClaims会根据加密算法和Claims对象来创建Token实例,这个实例中的Header就是之前提到的 JWT 三部分之一。signedString方法会利用传入的密钥生成签名字符串。它利用t.Signing Stringt.Method.Sign返回的字符串以.为分隔符拼装在一起并返回。

func GenerateToken(appKey, appSecret string) (string, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(global.JWTSetting.Expire)
	claims := Claims{
		AppKey:    util.EncodeMD5(appKey),
		AppSecret: util.EncodeMD5(appSecret),
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    global.JWTSetting.Issuer,
		},
	}

	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenClaims.SignedString(GetJWTSecret())
	return token, err
}
func (t Time) Unix() int64 {
	return t.unixSec()
}
func (t Time) Unix() int64 {
	return t.unixSec()
}
func EncodeMD5(value string) string {
	m := md5.New()
	m.Write([]byte(value))

	return hex.EncodeToString(m.Sum(nil))
}
func NewWithClaims(method SigningMethod, claims Claims) *Token {
	return &Token{
		Header: map[string]interface{}{
			"typ": "JWT",
			"alg": method.Alg(),
		},
		Claims: claims,
		Method: method,
	}
}
func (t *Token) SignedString(key interface{}) (string, error) {
	var sig, sstr string
	var err error
	if sstr, err = t.SigningString(); err != nil {
		return "", err
	}
	if sig, err = t.Method.Sign(sstr, key); err != nil {
		return "", err
	}
	return strings.Join([]string{sstr, sig}, "."), nil
}

SigningString会将 header(头部)和 payload(荷载)部分做一次 base64Url 编码,在下面的代码中,parts用于盛放编码后的字符串,最后利用strings.Join将这两个字符串以.为分隔符连接在一起。 t.Method.Sign利用它的签名算法(这里是jwt.SigningMethodHS256)、t.SigningString得到的字符串、密钥secret,生成一个签名字符串,即 JWT 三部分之一的 signature(签名)。由此可以看出,签名是由头部、荷载、密钥、加密算法共同生成的,因此可以用来校验消息是否被篡改,一旦被篡改,签名就无法对上。

func (t *Token) SigningString() (string, error) {
	var err error
	parts := make([]string, 2)
	for i, _ := range parts {
		var jsonValue []byte
		if i == 0 {
			if jsonValue, err = json.Marshal(t.Header); err != nil {
				return "", err
			}
		} else {
			if jsonValue, err = json.Marshal(t.Claims); err != nil {
				return "", err
			}
		}

		parts[i] = EncodeSegment(jsonValue)
	}
	return strings.Join(parts, "."), nil
}
func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) {
	if keyBytes, ok := key.([]byte); ok {
		if !m.Hash.Available() {
			return "", ErrHashUnavailable
		}

		hasher := hmac.New(m.Hash.New, keyBytes)
		hasher.Write([]byte(signingString))

		return EncodeSegment(hasher.Sum(nil)), nil
	}

	return "", ErrInvalidKeyType
}

3.解析校验Token

ParseTokenGenerateToken的反过程,它用来解析和校验 Token。该方法调用jwt.ParseWithClaims获取tokenClaims,然后对它进行格式的校验,并检查它是否有效,最终将Claims返回。ParseWithClaims用于解析鉴权的声明,它调用Parser.ParseWithClaims进行解码和校验,并返回*Token。Token结构体如下图所示,其中的Valid用于表示该 Token 是否有效,它的值与ExpiresAtIssuerNot Before有关。

func ParseToken(token string) (*Claims, error) {
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return GetJWTSecret(), nil
	})

	if tokenClaims != nil {
		claims, ok := tokenClaims.Claims.(*Claims)
		if ok && tokenClaims.Valid {
			return claims, nil
		}
	}

	return nil, err
}
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
	return new(Parser).ParseWithClaims(tokenString, claims, keyFunc)
}
type Token struct {
	Raw       string
	Method    SigningMethod
	Header    map[string]interface{} 
	Claims    Claims
	Signature string
	Valid     bool
}

三.总结

       经过这次的代码分析,我基本上掌握了Jwt的基本用法,Jwt可以让我们的访问变得更加安全,而且 Jwt的优点有很多,比如Jwt简洁,Token数据量小,传输速度快;因为JWT Token是以JSON加密形式保存在客户端的,所以原则上任何web形式都支持,不需要在服务端保存会话信息,也就是说不依赖于cookie和session,所以没有了传统session认证的弊端,特别适用于分布式微服务。本次主要分析的语言是go语言。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值