前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[方法]100来行实现一套灵活可扩展的 JWT 库

[方法]100来行实现一套灵活可扩展的 JWT 库

作者头像
小锟哥哥
发布2022-05-10 08:15:03
2490
发布2022-05-10 08:15:03
举报
文章被收录于专栏:GoLang全栈

JWT 全称 JSON Web Tokens 现在被广泛的应用于各种前后端分离的场景,它比传统的 Token Session 方式,更具灵活性。

网上有很多开源的 JWT 库,非常之多,官方开源组织也提供了开源库。

可以访问这网址去下载:

代码语言:javascript
复制
https://jwt.io

[图片截自jwt.io网站]

但是,如果我们知其然,而不知其所以然的使用,难免会遇到问题了不知道怎么解决。

所以我这次分享下,最近我自己写的一个 JWT 库,代码已经上传到 github 上了,地址如下:

代码语言:javascript
复制
https://github.com/liu578101804/go-tool/tree/master/jwt

相比那些出名的 JWT 库来说,我没有任何优势,初衷只是作为学习使用,欢迎大家指正其中的不足。

下面就给大家说下我的实现思路吧。

JWT 的原理

我们要想实现 JWT 算法就得先了解他的原理,我尽量用简短的话去解释:

下面来一段真实的由 JWT 算法生成的 Token 字符串:

代码语言:javascript
复制
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT 算法输出的数据是一串包含 header(头信息).payload(内容).signature(签名)的一段字符串。

是不是豁然开朗了,这串字符串又叫 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

代码语言:javascript
复制
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 的主体核心给写好了,现在只需要根据你想要的加密算法进行填充就好了。

下面我们就去实现一个最简单的 BS256 算法(生产环境一般不会使用),新建一个 bs.go 文件,内容如下:

代码语言:javascript
复制
package jwt

import (
	"crypto/sha256"
	"encoding/base64"
	"fmt"
)

func NewBS256() 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 很容易被人篡改和模仿,不能用于生产的

生产环境一般会使用会带有私钥的可逆加密算法去加密 header(头信息)和 payload(内容),比如: HMAC ,RSA ,再配合 MD5 或者 SHA 算法进行签名。而不会像上面的这段,用 BASE64 去加密内容,这就等同于把你 token 里面的内容公开透明化了。

写到这里我们的分享也就结束了,赶快去实现你的 JWT 算法吧,有任何疑问欢迎留言。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GoLang全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JWT 的原理
  • 算法组成
  • 具体实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档