dgrijalva / jwt-go可以对MapClaims提出主张,但不能对StandardClaims提出主张吗?

时间:2018-09-22 19:39:23

标签: go jwt

我正在使用以下代码创建令牌

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
    Subject: string(user.Id),
})

tokenString, err := token.SignedString([]byte("secret"))

并尝试使用以下代码解析它们

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)

为什么我不能将我的声明投向StandardClaims并访问Claims.Subject?

1 个答案:

答案 0 :(得分:3)

  

为什么我不能对StandardClaims声明所有权并访问claims.Subject

从概念上讲,这是不可能的,因为jwt.Parse函数by default将声明解析为jwt.MapClaims的实例。这是与jwt.StandardClaims根本不同的数据结构;编译器无法使用简单的类型转换在两者之间自动转换,因为它们表示数据的方式不同。

解决方案

该库提供了ParseWithClaims函数,该函数允许您为要声明为的声明指定jwt.Claims接口的自己的实现者。您可以传递jwt.StandardClaims的实例。例如:

token, err := jwt.ParseWithClaims(
    tokenString, &jwt.StandardClaims{},
    func(token *jwt.Token) (interface{}, error) {
        // ...
    },
)

如果可能,将对声明进行解析并将其解码为变量token.Claims。存储在此变量中的值的基础类型(动态 1 )类型为*jwt.StandardClaims。可以在类型断言中使用它来从接口类型中恢复标准声明:

claims, ok := token.Claims.(*jwt.StandardClaims)
if !ok {
    // handle type assertion failure
}
// do something with "claims"

让我们进一步研究语言规范和库定义,以便对该要求进行更严格的评估。

对包类型的背景了解

jwt.MapClaims是具有基础类型map[string]interface{}code)的已定义类型。

jwt.StandardClaims是已定义的struct类型(code):

type StandardClaims struct {
    // Field set elided for brevity, as it is unimportant to the
    // answer.
}

这两种类型都实现了jwt.Claims接口类型(definition),因此可以分配给类型为jwt.Claims的变量:

type Claims interface {
    Valid() bool
}

Token结构的Claims类型为jwt.Claims的{​​{3}} –任何实现Claims接口的值都可以分配给{{1} }。

类型断言定义

Claims不是接口类型(动态类型 <)时,形式为x.(T)的类型声明表达式的语言规范field是有效的T的sup> 1 必须与类型x 相同。在这里,您希望评估断言T;即声明的类型不是接口类型。

x.(*jwt.StandardClaims)的{​​{3}}最终在默认解析器上调用jwt.Parse,传入jwt.ParseWithClaims的实例作为声明目的地:

jwt.MapClaims

因此,结果令牌中func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) } 字段的动态类型为Claims。此类型与类型jwt.MapClaims (即不完全相同),因为用户定义的类型与除其自身之外的其他任何类型的specifies不同。因此,类型断言失败。


1 动态类型code):在Go中回想起,接口类型可以由实现以下类型的超集的任何类型隐式实现:接口中指定的方法。如果我们定义类型为jwt.StandardClaims的接口,则变量声明MyInterface具有静态类型(在编译时定义)var x MyInterface。但是,在运行时,我们可以将实现MyInterface的任何值分配给MyInterface。随时分配给x的值的基础类型(实现接口的类型)指定变量的动态类型