当前位置: 首页 > 科技观察

Golang在不到100行的时间内实现了一个灵活的JWT库

时间:2023-03-13 07:36:41 科技观察

JWT全陈JSONWebTokens现在广泛应用于各种前后端分离的场景。它比传统的TokenSession方法更灵活。当然网上也有很多开源的JWT库,数量很多,而且开源组织也提供官方库。可以访问这个网站下载:https://jwt.io但是如果我们知道了却不知道为什么要用,难免会出现很多问题。所以这次分享一下,最近自己写了一个JWT库,代码已经上传到github,地址如下:https://github.com/liu578101804/go-tool/tree/master/jwt比较有名JWT库方面,我没有任何优势,仅供学习,不足之处欢迎大家指正。说说我的实现思路吧。JWT的原理如果要实现JWT算法,首先要了解它的原理。我尽量简单的解释一下:JWT算法输出的数据是一串header(头部信息).payload(内容).signature(签名)的字符串。来一段真实的JWT生成的字符串:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9。eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c是不是豁然开朗了,这串字符串又叫token,因为这串token里面包含了验证需要的信息,相比传统的session需要到服务器里面Itismoreflexibleandindependenttoobtainverificationinformation.因为它不依赖session等传统存储,所以有很大的优势,不用在分布式服务器中。当然,他也有缺点。最头疼的两点是:不可控。代币一旦发行,就不能提前销毁。不像传统的,我可以在我的session中删除token,下次回来的时候token就失效了。当然,这也不是没有办法解决。就是给整个系统加上一个黑名单机制,但是有点麻烦。与传统的session数据量和隐私相比,信息量就没那么好了。因为token一般不建议特别长,payload携带的数据量是有限的。同时字符串中的payload是可以被解密的,所以存在一定的被破译的风险(当然你可以使用更难的算法来降低这种风险)。算法构成JWT的算法构成非常简单。它只需要一个可逆的加密算法对header(头部信息).payload(内容)进行加密,以及一个不可逆的算法对前面的部分内容进行加密签名,生成签名(signature)。如果我们组合不同的加密算法,就会形成不同的JWT加密算法。例如:HS256(HMAC+SHA-256)RS256(RSA+SHA-256)当然还有很多,你可以自己组合,我们写的库支持你的自定义。具体实现开始进入代码实现阶段:下面说一下我的设计思路。Golang有一个天然的优势,它支持将函数作为变量传入。我们可以让调用者根据这个特性来实现加密部分。我们只实现主体,这样我们的JWT就非常灵??活。我们要写的代码主体不到100行,没有注释和空行。jwt.gopackagejwtimport("encoding/json""strings""fmt")//声明一个标准的JWT接口typeIJwtinterface{//设置headerSetHeader(string)//设置签名算法SetSignFunc(SignFunc)//设置编码算法)//写入bodyWriteBody(map[string]interface{})//生成jwtCreateJwtString()(string,error)//验证jwtCheckJwtString(string)bool}//指定header的格式typeHeaderstruct{Typestring`json:"type"`Algstring`json:"alg"`}//签名算法typeSignFuncfunc([]byte)string//编码算法typeEncodeFuncfunc([]byte)string//声明一个结构图实现标准JWT接口typeJwtstruct{HeaderHeaderBodymap[string]interface{}signFunSignFuncencodeFunEncodeFunc}//设置header信息,表示你使用的签名算法func(j*Jwt)SetHeader(headerTypestring){j.Header=Header{Type:"JWT",Alg:headerType,}}//设置签名算法func(j*Jwt)SetSignFunc(signFuncSignFunc){j.signFun=signFunc}//设置加密algorithmforheaderandbodyfunc(j*Jwt)SetEncodeFunc(encodeFuncEncodeFunc){j.encodeFun=encodeFunc}//写入要加密的内容func(j*Jwt)WriteBody(bodymap[string]interface{}){j.Body=body}//生成tokenfunc(j*Jwt)CreateJwtString()(string,error){//编码headerheaderByte,err:=json.Marshal(j.Header)iferr!=nil{return"",err}headerStr:=j.encodeFun(headerByte)//编码bodyByte,err:=json.Marshal(j.Body)iferr!=nil{return"",err}bodyStr:=j.encodeFun(bodyByte)//签名signByte:=j.signFun([]byte(string(headerStr)+"."+string(bodyStr)))returnfmt.Sprintf("%s.%s.%s",headerStr,bodyStr,signByte),nil}//验证token是否合规func(j*Jwt)CheckJwtString(inputstring)bool{arr:=strings.Split(input,".")//格式是否正确iflen(arr)!=3{returnfalse}//签名signByte:=j.signFun([]byte(string(arr[0])+"."+string(arr[1])))ifstring(signByte)!=arr[2]{returnfalse}returntrue}这个文件已经写好了JWT的核心,现在只需要只需填写所需的加密算法接下来我们实现最简单的RS256算法,新建一个bs.go文件,内容如下:/Sha256jwtM.SetSignFunc(func(bytes[]byte)string{h:=sha256.New()h.Write(bytes)returnfmt.Sprintf("%x",h.Sum(nil))})//base64jwtM.SetEncodeFunc(func(bytes[]byte)string{returnbase64.URLEncoding.EncodeToString(bytes)})return&jwtM}这里的header和payload是base64加密的,签名是sha256的。当然,这种算法生成的JWT容易被复制和模仿,不能用于生产。我们的分享到这里就结束了,抓紧时间实现你的JWT算法,有什么问题欢迎留言。