什么JWT
JWT(JSON Web Token)是一种开放标准(RFC 7519),定义了一种在各方之间安全传输信息的简洁方式。这些信息可以被验证和信任,因为它们是数字签名的。
JWT由三部分组成,用.
分隔。它们分别是Header(头部)、Payload(负载)和Signature(签名)。
Header:通常由两部分组成:token类型和使用的加密算法。
Payload:存放有效信息的地方。有效信息包括三个部分:声明类型、公共声明和私有声明。
Signature:签名是由头部、负载、密钥和加密算法组合而来,用于验证消息没有被篡改。
在网络应用中,JWT通常用于鉴权。服务器生成一个token返回给客户端,客户端以后的每次请求都会带上这个token,服务器通过这个token识别和验证客户端的身份。由于JWT内部包含了识别客户端的信息,并且被加密,所以是安全的。同时,JWT也是无状态的,服务器不需要存储任何客户端的信息,这对于分布式应用来说是一个很大的优势。
优点:
简洁:JWT是为了在网络应用环境间传递声明而执行的。因为是轻量化的,所以它可用于SSO(Single Sign On单点登录)。
安全:JWT的ClAIms(声明)是被加密的,采用的是一个密钥,只有签发者才能使用那个密钥,这保证了安全。
方便:无状态,不需要在服务器存储用户状态,这使得应用更容易扩展。
缺点:
体积较大:因为JWT需要在每次请求头上带上token,如果token过大可能会导致请求头过大,不如sessionID方式中uuid那么轻便。
存储敏感信息:虽然JWT的Payload(负载)部分采取了Base64Url 编码进行转义,但这仅仅是编码而已,并非加密操作,需要避免在声明中放入敏感信息。
无自动过期机制:JWT一旦签发在到期时间之前会一直有效,不像session那样在用户登出之后可以直接销毁。这就需要服务器提供黑名单机制来强制让token失效。
总的来说,在很多场合,尤其是分布式无状态应用,JWT是一个很好的选择,能够提供便捷的权限管理功能。但同时也要注意其潜在的安全问题,尤其是避免将敏感信息放在payload中,并提供充分的安全防护,例如采用HTTPS、定期更换秘钥、提供token黑名单机制等。不过虽然JWT的缺点不能直接销毁,但是可以通过在服务端存储key的形式,通过每次获取请求头然后比对缓存中的值一旦缓存值过期则表示失效可以间接避免缺点3.
Go中如何使用JWT
安装jwt-go库:在Go语中,有一个非常优秀的处理JWT
的库jwt-go
,可以直接使用go get
命令进行安装:
go get Github.com/dgrijalva/jwt-go
3.我们需要定义一个结构体,表示我们自定义的 Claims。这个结构体需要嵌入 jwt.StandardClaims
,然后添加我们需要的字段,比如 UserID:
func main() {
v1, err := CreateTokenV1("456", false)
fmt.Printf(v1,err)
ParseTokenV2(v1,"456")
}
func CreateTokenV1(secretKey string, isNotExpired bool) (string, error){
var expirationTime = time.Now().Add(24 * time.Hour)
if isNotExpired {
expirationTime = time.Now().Add(40 * 24 * time.Hour)
}
claims := &MyCustomClaims{
jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
},
123,//用户id
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secretKey))
}
func ParseTokenV2(tokenString string, secretKey string){
token, _ := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(t *jwt.Token) (interface{}, error) {
return []byte(secretKey), nil
})
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
fmt.Println(claims.UserID)
}
}
type MyCustomClaims struct {
jwt.StandardClaims
UserID int `json:"user_id"`
}
接着,在生成 token 时,我们需要创建一个 MyCustomClaims 的实例,设置 UserID,且此时可以设置 token 的有效期(ExpireAt)等其他标准字段:
最后,在解析 token 时,我们需要解析到我们的自定义 Claims 结构体中,这样就可以获取用户 ID 了:
CreateTokenV1和ParseTokenV2分别代表着创建和解析
以上只是一种最基本的实现方式,在实际应用场景中,一般会在claims
中包含更多信息,比如用户ID、用户名等。同时也需要对错误情况进行更多处理