我正在使用以下代码创建令牌
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
Subject: string(user.Id),
})
tokenString, err := token.SignedString([]byte("secret"))
Run Code Online (Sandbox Code Playgroud)
并尝试使用以下代码解析它们
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, UnauthorizedError
}
return []byte("secret"), nil
})
if err != nil {
return -1, UnauthorizedError
}
if !token.Valid {
return -1, UnauthorizedError
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return -1, UnauthorizedError
}
logrus.Info(claims)
Run Code Online (Sandbox Code Playgroud)
为什么我不能将我的声明转换为 StandardClaims 并访问 claim.Subject?
为什么我不能将我的声明投射到
StandardClaims和访问claims.Subject?
从概念上讲,这是不可能的,因为该jwt.Parse函数默认将声明解析为jwt.MapClaims. 这是一个根本不同的数据结构jwt.StandardClaims;编译器无法使用简单的类型转换在两者之间自动转换,因为它们表示数据的方式不同。
该库提供了该ParseWithClaims功能,该功能允许您指定自己的jwt.Claims接口实现者,以便将声明解码为。您可以传递jwt.StandardClaims. 例如:
token, err := jwt.ParseWithClaims(
tokenString, &jwt.StandardClaims{},
func(token *jwt.Token) (interface{}, error) {
// ...
},
)
Run Code Online (Sandbox Code Playgroud)
如果可能,声明将被解析并解码到变量中token.Claims。存储到此变量中的值的基础(动态1)类型将为*jwt.StandardClaims. 这可用于类型断言以从接口类型恢复标准声明:
claims, ok := token.Claims.(*jwt.StandardClaims)
if !ok {
// handle type assertion failure
}
// do something with "claims"
Run Code Online (Sandbox Code Playgroud)
让我们更深入地研究语言规范和库定义,以对该声明进行更严格的评估。
jwt.MapClaims是具有基础类型map[string]interface{}( code ) 的已定义类型。
jwt.StandardClaims是一个定义的struct类型(代码):
type StandardClaims struct {
// Field set elided for brevity, as it is unimportant to the
// answer.
}
Run Code Online (Sandbox Code Playgroud)
这两种类型都实现了jwt.Claims接口类型(定义),因此可以分配给类型为的变量jwt.Claims:
type Claims interface {
Valid() bool
}
Run Code Online (Sandbox Code Playgroud)
该Token结构体有一个名为type的字段- 任何实现接口的值都可以分配给.Claimsjwt.ClaimsClaimsClaims
的语言规范指定为以下形式的类型断言表达式x.(T)时是有效的T是不是一个接口类型,动态类型1的x必须是相同的类型T。在这里,您希望评估断言x.(*jwt.StandardClaims);即断言类型不是接口类型。
该代码为jwt.Parse最终调用jwt.ParseWithClaims的默认解析器,传递的一个实例jwt.MapClaims是权利要求的目标:
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
}
Run Code Online (Sandbox Code Playgroud)
因此Claims生成的令牌中字段的动态类型是 type jwt.MapClaims。此类型与type不同(即不相同)jwt.StandardClaims,因为用户定义的类型总是与除自身之外的任何其他类型不同。因此,类型断言失败。
1动态类型(ref):回想一下,在 Go 中,接口类型由实现接口中指定方法的超集的任何类型隐式实现。如果我们定义一个 type 的接口MyInterface,则变量声明var x MyInterface具有静态类型(在编译时定义)MyInterface。然而,在运行时,我们可以指定它实现的任何值MyInterface来x。x任何时刻赋值的基础类型(实现接口的类型)指定变量的动态类型。