如何用Golang实现一套灵活的JWT库
如何用Golang实现一套灵活的JWT库,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
JWT 全 chen JSON Web Tokens 现在被广泛的应用于各种前后端分离的场景,他比传统的 Token Session 方式,更具灵活性。
当然网上也有很多开源的 JWT 库,非常之多,开源组织也提供了官方的库。
我最近自己写的一个 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 需要到服务器里面去取验证的信息,更加的灵活独立。
因为他不依赖 session 这种传统的存储,别用在分布式服务器里面有着很大的优势。
当然他也有缺点,最头痛就这 2 点:
不可控。一旦签发出去的 token 将无法提前让他销毁,不像传统的,我可以把我 session 里面的 token 删了下次过来,token 就失效了。当然这也不是没办法解决,在整个体系里面加入黑名单机制就行,只是稍微麻烦了点。
信息相对传统的 session 数据量和隐私性没那么好。因为 token 一般都不建议特别长,所以 payload 承载的数据量是有限的。同时字符串里面的 payload 是可以被解密的,所以存在一定性的被破译风险(当然你可以使用比较难破译的算法去降低这个风险)。
算法组成
JWT 的算法组成很简单,只需要 一个可逆的加密算法去加密 header(头信息).payload(内容),一个不可逆的算法去对前面这部分内容进行加密签名生成 signature(签名) 就行。
如果我们用不同的加密算法组合便形成了不同的 JWT 加密算法。比如:
HS256 (HMAC + SHA-256)
RS256 (RSA + SHA-256)
当然还有很多,你可以自己去组合,我们将写的这个库支持你自定义。
具体实现
下面就开始进入代码实现阶段了:
说下我的设计思路,Golang 他有一个天然的优势就是支持把函数作为变量传入,我们便可以根据这一特性把加密部分让调用者去实现,我们把实现主体就行,这样便我们的 JWT 便非常灵活了。
我们要写的主体代码去掉注释空行不到 100 行。
jwt.go
package jwt import ( "encoding/json" "strings" "fmt" ) //声明一个标准的JWT接口 type IJwt interface { //设置头部 SetHeader(string) //设置签名算法 SetSignFunc(SignFunc) //设置编码算法 SetEncodeFunc(EncodeFunc) //写入body WriteBody(map[string]interface{}) //生成jwt CreateJwtString() (string,error) //验证jwt CheckJwtString(string) bool } //规范header的格式 type Header struct { Type string `json:"type"` Alg string `json:"alg"` } //签名算法 type SignFunc func([]byte) string //编码算法 type EncodeFunc func([]byte) string //声明一个结构图 去实现 标准的JWT接口 type Jwt struct { Header Header Body map[string]interface{} signFun SignFunc encodeFun EncodeFunc } //设置头部信息,说明你使用的签名算法 func (j *Jwt) SetHeader(headerType string){ j.Header = Header{ Type: "JWT", Alg: headerType, } } //设置签名算法 func (j *Jwt) SetSignFunc(signFunc SignFunc) { j.signFun = signFunc } //设置对 header 和 body 的加密算法 func (j *Jwt) SetEncodeFunc(encodeFunc EncodeFunc) { j.encodeFun = encodeFunc } //写入要加密的内容 func (j *Jwt) WriteBody(body map[string]interface{}) { j.Body = body } //生成token func (j *Jwt) CreateJwtString() (string,error) { //编码header headerByte,err := json.Marshal(j.Header) if err != nil { return "",err } headerStr := j.encodeFun(headerByte) //编码body bodyByte,err := json.Marshal(j.Body) if err != nil { return "",err } bodyStr := j.encodeFun(bodyByte) //签名 signByte := j.signFun([]byte(string(headerStr)+"."+string(bodyStr))) return fmt.Sprintf("%s.%s.%s",headerStr,bodyStr,signByte),nil } //验证 token 是否合规 func (j *Jwt) CheckJwtString(input string) bool { arr := strings.Split(input,".") //格式是否正确 if len(arr) != 3 { return false } //签名 signByte := j.signFun([]byte(string(arr[0])+"."+string(arr[1]))) if string(signByte) != arr[2] { return false } return true }
这个文件就已经把 JWT 的核心给写好了,现在只需要根据你想要的加密算法进行填充就好了。
下面我们就去实现一个最简单的 RS256 算法,新建一个 bs.go 文件,内容如下:
package jwt import ( "crypto/sha256" "encoding/base64" "fmt" ) func NewRS256() IJwt { jwtM := Jwt{} //Sha256 jwtM.SetSignFunc(func(bytes []byte) string { h := sha256.New() h.Write(bytes) return fmt.Sprintf("%x",h.Sum(nil)) }) //base64 jwtM.SetEncodeFunc(func(bytes []byte) string { return base64.URLEncoding.EncodeToString(bytes) }) return &jwtM }
我这里 header 和 payload 采用 base64 去加密,签名采用 sha256 当然这种算法生成的 JWT 很容易被人串改模仿,不能用于生产的。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注编程网行业资讯频道,感谢您对编程网的支持。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341