在golang中构建JWT库

本文假设您是
1.程序员
2.对JWT是什么有一个扎实的认识
3.了解基本的golang

智威汤逊简介
JWT由三部分组成
1. 标头 -定义用于哈希签名和JWT类型的算法。 我们将使用HS256
其他包括HS384,HS512,RS2556

2. 有效负载-包含有关发行者,到期日期,令牌用户等信息,例如到期日期(ESP)

3. 签名—包含编码的标头和有效负载的串联值,使用标头中的算法生成哈希。 返回的哈希是签名

所有这些都用点连接起来,形成令牌。 输出看起来像这样。

  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOixMjM0NTY3OD.kwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydgVHEJHYZHYBHJFYHJHYHYBJF95B 

让我们编码
根据上面的解释,JWT令牌由三部分组成。 第一部分和第二部分是base64Encode标头,其中包含所用算法的ALGO和TYP,第二部分由有效负载组成。
贝娄(Bellow)是该工作所需的内部软件包的列表

 导入( 
“加密/ hmac”
“ crypto / sha256”
“ encoding / base64”
“ encoding / json”
“错误”
“弦”
“时间”
)// Base64Encode接受一个字符串,并返回一个以64为基数编码的字符串func Base64Encode(src string)string {
返回字符串。
TrimRight(base64.URLEncoding。
EncodeToString([] byte(src)),“ =”)
} // Base64Encode接受一个以64为基数的编码字符串,并返回// actual字符串,否则错误将无法对该字符串进行解码
func Base64Decode(src string)(string,error){
如果:= len(src)%4; l> 0 {
src + = strings.Repeat(“ =”,4-l)
}
解码,错误:= base64.URLEncoding.DecodeString(src)
如果err!= nil {
errMsg:= fmt.Errorf(“解码错误%s”,err)
返回“”,errMsg
}
返回字符串(已解码),无
}

以上两个功能是不言自明的。 第一个编码一个字符串,另一个解码一个base64编码的字符串

现在我们可以对字符串进行编码和解码了,让我们创建一个哈希并验证哈希完整性的方法

  //哈希使用密钥生成字符串的Hmac256哈希 
func Hash(src字符串,秘密字符串)字符串{
键:= [] byte(secret)
h:= hmac.New(sha256.New,键)
h.Write([] byte(src))
返回base64.StdEncoding.EncodeToString(h.Sum(nil))
}
```// isValidHash再次验证哈希值
func isValidHash(值字符串,哈希字符串,秘密字符串)bool {
返回哈希==哈希(值,秘密)
}
注意:哈希不能被逆转,您所能做的就是将相同字符进行哈希运算并将其与哈希值进行比较。 如果计算结果为true,则字符为哈希中的字符。isValidHash函数仅将值与密钥哈希在一起,并与哈希值进行汇总

在上面,我们创建了两种方法,一种用于生成HS256哈希,另一种用于根据哈希验证字符串。

现在开始有趣
让我们创建一个令牌。 为此,我们将创建两个函数
1.编码-:这将创建JWT
2.解码-:验证,解码并返回有效载荷。

编码

  //编码会生成一个jwt。 
func Encode(有效载荷有效载荷,机密字符串)字符串{
输入Header struct {
Alg字符串`json:” alg”`
输入字符串`json:” typ”`
}标头:=标头{
Alg:“ HS256”,
类型:“ JWT”,
}
str,_:= json.Marshal(标题)
标头:= Base64Encode(string(str))
encodePayload,_:= json.Marshal(有效载荷)
signatureValue:=标头+“。” +
Base64Encode(string(encodedPayload))
返回signatureValue +“。” +哈希(signatureValue,秘密)
}标头不是参数,因为我们仅支持一个ALGO。 理想的情况是指定要使用的算法类型。

编码可能不是最好的名称,但功能非常简单1. Base64对标头和有效负载进行编码 ,并用点将其连接起来。 称它为signatureValue
2.散列signatureValue并将其称为秘密
3.用一个点将signatureValue密钥连接起来并返回

  signatureValue:= Base64Encode(header)+“。” + Base64Encode(string(encodedPayload)) 
返回signatureValue +“。” + Hmac256(signatureValue,秘密)

解码
现在我们可以进行编码了,只需要验证和解码有效负载即可。

  func Decode(jwt字符串,秘密字符串)(接口{},错误){ 
token:= strings.Split(jwt,“。”)//检查jwt令牌是否包含
//标头,有效负载和令牌
如果len(token)!= 3 {
splitErr:= errors.New(“无效令牌:令牌应包含标头,有效载荷和机密”)
返回nil,splitErr
} //解码有效载荷
encodedPayload,PayloadErr:= Base64Decode(令牌[1])
如果PayloadErr!= nil {
返回nil,fmt.Errorf(“有效负载:%s”,PayloadErr.Error())
} payload:= Payload {} //将有效负载从字符串解析为结构
ParseErr:= json.Unmarshal([] byte(decodedPayload),&payload)如果ParseErr!= nil {
返回nil,fmt.Errorf(“有效载荷:%s”,ParseErr.Error())
} //检查令牌是否已过期。
如果payload.Exp!= 0 && time.Now()。Unix()> payload.Exp {
返回nil,errors.New(“令牌已过期:令牌已过期”)
} signatureValue:= token [0] +“。” + token [1] //验证标头和签名是否完全是
// 签名
如果CompareHmac(signatureValue,token [2],secret)==假{
返回nil,errors.New(“无效令牌”)
返回有效载荷,无
}

上面的函数接受一个令牌和一个秘密,并执行下面列出的过程
1.分割令牌
2.确认令牌是否包含三个部分
3.解码有效载荷
4.检查令牌是否尚未过期
5.检查令牌是否有效
7.返回有效载荷。

如果1–6通过,您将获得有效的有效载荷。 如果anyoint这些检查中的任何一项失败,您都会得到一个错误。 显示您的令牌无效

进行令牌验证的另一方法值得商,,但是无论如何,我们确实检查了多种情况以确保令牌有效。

我们的图书馆在行动
创建一个example.go文件

 包mainimport( 
“ fmt”
“ jwt”
“时间”
)func main(){//我们的秘密
secret:=“ randpmly生成的秘密”类型Meta struct {
名称字符串
电邮字串
}有效载荷:= jwt.Payload {
子:“ 123”,
Exp:time.Now()。Unix()+ 100000,
公开:元{
名称:“墨菲”
电子邮件:“ Murphy@jwt.com”,
},
}令牌:= jwt.Encode(有效载荷,秘密)fmt.Println(令牌)
//打印出eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE1NDEyNzAyNzAsInB1YmxpYyI6eyJOYW1lIjoiTXVycGh5IiwiRW1haWwiOiJNdXJwaHlAand0LmNvbSJ9fQ ==。dkzber79rM7gubpPCaAkjz0gFjxndbMCk6zQWrswkzE = fmt.Println(jwt.Decode(令牌,秘密))
//打印出我们的有效载荷
123 1541270653 map [名称:Murphy电子邮件:Murphy@jwt.com]} ​​
}

我为Chrome使用了JWT调试器工具来验证令牌。 为了进一步证明我们的图书馆有效。 您可以自己检查一下。

您可以在此处找到完整的代码

结论
JWT是一个很棒的概念,如果正确实施,它是安全的,并且已经改变了大多数现代应用程序的构建方式。 Golang是一种很棒的语言,但是您始终可以尝试使用自己喜欢的语言来构建该库。
此处显示的概念可以应用在您选择的任何流行语言中。

尽管此库有效,但在生产中我还是希望使用经过良好测试和支持的库。 这个想法是向您展示JWT的内部原理。