持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
jwt的特点
简洁,不占用服务端存。
cookie和session
http
协议无状态的,每次请求对于服务端来说都是一个独立的,早期保存用户信息使用的是cookie
和session
,cookie
存储在客户端,存一个sessionid
每次请求时携带上,session
存储在后端,每次通过请求携带过来的sessionId
去找对应的session数据
,判断用户信息。
这种方式存在一些弊端,session
是基于cookie
的。cookie
不支持跨域\用户禁用cookie
时\用户使用的是移动设备,一但cookie
无法使用,那么session
也会无法使用。
token
token
的出现解决了这一问题,他将鉴权信息放在请求头部,在http
头部拿到信息后进行用户验证,这就解决了cookie
无法使用时的验证用户信息的问题
jwt
jwt
是对token
的进一步改造。它与token
相比, token
数据存在服务端, jwt
存储在客户端。jwt
不会占用服务器资源,但是他会消耗cpu
的处理(与token
相比)
jwt 的组成
jwt
由头部(head)、用户信息(claim)、签名(signal)三个部分通过.
拼接后再用base64编码成的一段字符串。
jwt的交互过程
-
jwt的获取
客户端发送身份信息到服务端,服务端校验完成后将部分用户信息加密,生成一个jwt字符串给客户端。客户端获得一个jwt字符串。
2. 业务请求中携带上jwt
获取到jwt字符串后,每次请求都携带上它,用于标识身份。
jwt在服务端的验证过程
服务端获取到jwt字符串后,
- 先将字符base64解码,拆分成三部分:
head
、claim
、sign
。 head
中存放了sign
的加密方式,通过将cliam
与服务端的密钥(secret
)通过head
中的加密方式加密,生成一个sign2
- 将
sign2
与客户端的sign
比较(sign2 == sign ?
),如果一致说明是签发的jwt
。不一致则说明发送的字符串有问题 - 再检验jwt的时效,是否过期
在golang使用
命令行下安装jwt模块
go get github.com/golang-jwt/jwt/v4
生成jwt
-
声明claim,存储
用户信息
和jwt信息
-
生成token对象
-
使用token对象的
SignedString
方法生成最终的token字符串
// 略...
// 创建 Claims ,MyCliams是自定义的jwt结构体
claims := &MyCliams{
params.Name,
params.Age,
jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), // 过期时间
Issuer: "", // 签发人
},
}
// 生成token对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 生成签名字符串
tokenstr, err := token.SignedString(secret)
// 略...
验证jwt
// 略...
/**
@ params.Token 是客户端发送过来的token字符串
@ &claim 是存放结果的结构体
*/
token, err := jwt.ParseWithClaims(params.Token, &claim, func(token *jwt.Token) (i interface{}, err error) {
// secret 是服务端加密使用的密钥,生成时也是用这把
return secret, nil
})
// 返回的是接口,将它断言为自定义结构体指针
cl, ok := token.Claims.(*MyCliams);
// 略...
客户端重复申请jwt问题
客户端一直访问登录接口,服务端因为没有记录,无法判断用户是否登录过。
总结
jwt是随着不同业务需要,演化而来。相比于cookie和token,jwt并不占用服务器存储,切安全,能够支持单点登录。但也是它不占用服务器资源的有点,导致它对jwt的控制不完整,他只能颁发而无法主动销毁,除非在服务端存下记录。