首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用jwt-go解析令牌

使用jwt-go解析令牌
EN

Stack Overflow用户
提问于 2014-09-15 12:31:42
回答 2查看 9.4K关注 0票数 9

有人能告诉我为什么下面的例子(从https://github.com/dgrijalva/jwt-go)不起作用吗?

代码语言:javascript
运行
复制
token, err := jwt.Parse(myToken, func(token *jwt.Token) ([]byte, error) {
    return myLookupKey(token.Header["kid"])
})

if err == nil && token.Valid {
    deliverGoodness("!")
} else {
    deliverUtterRejection(":(")
}

我说cannot use func literal (type func(*jwt.Token) ([]byte, error)) as type jwt.Keyfunc in argument to jwt.Parse时出错了

我尝试使用来自几个不同的jwt-go示例的代码,但最后总是出现同样的错误。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-09-15 12:38:03

Parse期望的函数

代码语言:javascript
运行
复制
type Keyfunc func(*Token) (interface{}, error)

您需要在函数文本中返回interface{},而不是byte[]

(可能使用byte.Buffer包装byte[],然后可以将其读入“将任意Golang接口转换为字节数组”中)

格特·凯肯斯第36期的注释中指出:提交e1571c8应该已经更新了这个示例。

其他的例子,如这个要点也需要更新。

票数 9
EN

Stack Overflow用户

发布于 2021-05-27 15:09:48

正如在另一个答案中提到的,is github.com/dgrijalva/jwt-go的最新版本是v3.2.0+incompatible。文档现在已经过时了,因为包的jwt.Keyfunc函数现在有了以下签名:

代码语言:javascript
运行
复制
type Keyfunc func (*Token) (interface{}, error)

在解析JWT时,这个jwt包也会对它们进行身份验证。对JWT进行身份验证需要两件事。

  1. JWT本身。
  2. 用于对JWT进行签名的加密密钥。通常,这是一个公钥。

这就是jwt.Keyfunc适合的地方。interface{}的返回值是稍后向加密密钥断言的类型。对于许多用例,这将是一个基于RSA或ECDSA的算法。这意味着返回类型通常是*ecdsa.PublicKey*rsa.PublicKey。(也可以使用HMAC密钥,但我不会讨论这个用例。)

如果您的公钥是PEM格式的,您可能对使用内置于jwt包中的函数感兴趣:ParseECPublicKeyFromPEMParseRSAPublicKeyFromPEM

创建公钥数据结构

如果您的公钥是另一种格式,则可能需要通过填充它们导出的字段来构建*ecdsa.PublicKey*rsa.PublicKey Go结构。

ECDSA

下面是要填充的数据结构:

代码语言:javascript
运行
复制
// PublicKey represents an ECDSA public key.
type PublicKey struct {
    elliptic.Curve
    X, Y *big.Int
}

嵌入式elliptic.Curve是通过使用与其长度关联的crypto/elliptic函数创建的:

代码语言:javascript
运行
复制
// Create the ECDSA public key.
publicKey = &ecdsa.PublicKey{}

// Set the curve type.
var curve elliptic.Curve
switch myCurve {
    case p256:
        curve = elliptic.P256()
    case p384:
        curve = elliptic.P384()
    case p521:
        curve = elliptic.P521()
}
publicKey.Curve = curve

对于XY,它们都是*big.Int的。如果您有这些整数的字节,则创建一个新的*big.Int并使用SetBytes方法。

代码语言:javascript
运行
复制
publicKey.X = big.NewInt(0).SetBytes(xCoordinate)
publicKey.Y = big.NewInt(0).SetBytes(yCoordinate)

RSA

下面是要填充的数据结构:

代码语言:javascript
运行
复制
// A PublicKey represents the public part of an RSA key.
type PublicKey struct {
    N *big.Int // modulus
    E int      // public exponent
}

模,N是一个*big.Int。如果有此整数的字节,则创建一个新的*big.Int并使用SetBytes方法。

代码语言:javascript
运行
复制
publicKey.N = big.NewInt(0).SetBytes(modulus)

指数是一个正则整数。因此,这应该是严格向前的,但是如果您有这个整数的字节,则可以创建如下所示的整数:

代码语言:javascript
运行
复制
publicKey.E = int(big.NewInt(0).SetBytes(exponent).Uint64())

创建一个jwt.Keyfunc

现在公钥在正确的数据结构中,现在是创建jwt.Keyfunc的时候了。如前所述,该函数的输入将是*jwt.Token,输出将是*ecdsa.PublicKey*rsa.PublicKey,但输入为空接口:interface{}error

*jwt.Token包含JWT本身,但是识别它与哪个公钥相关联的最佳方法是kidkid是JWT头中包含的字符串值。阅读关于参数在RFC中。的文章。它可以从JWT中读到如下:

代码语言:javascript
运行
复制
// ErrKID indicates that the JWT had an invalid kid.
ErrKID := errors.New("the JWT has an invalid kid")

// Get the kid from the token header.
kidInter, ok := token.Header["kid"]
if !ok {
    return nil, fmt.Errorf("%w: could not find kid in JWT header", ErrKID)
}
kid, ok := kidInter.(string)
if !ok {
    return nil, fmt.Errorf("%w: could not convert kid in JWT header to string", ErrKID)
}

此时,输入是kid,输出是公钥。此时,jwt.Keyfunc最简单的实现是一个从map[string]interface{}读取的函数,其中键stringkid,值interface{}是它的公钥。

下面是一个例子:

代码语言:javascript
运行
复制
// ErrKID indicates that the JWT had an invalid kid.
ErrKID := errors.New("the JWT has an invalid kid") // TODO This should be exported in the global scope.

// Create the map of KID to public keys.
keyMap := map[string]interface{}{"zXew0UJ1h6Q4CCcd_9wxMzvcp5cEBifH0KWrCz2Kyxc": publicKey}

// Create a mutex for the map of KID to public keys.
var mux sync.Mutex

// Create the jwt.Keyfunc
var keyFunc jwt.Keyfunc = func(token *jwt.Token) (interface{}, error) {

    // Get the kid from the token header.
    kidInter, ok := token.Header["kid"]
    if !ok {
        return nil, fmt.Errorf("%w: could not find kid in JWT header", ErrKID)
    }
    kid, ok := kidInter.(string)
    if !ok {
        return nil, fmt.Errorf("%w: could not convert kid in JWT header to string", ErrKID)
    }

    // Get the appropriate public key from the map of KID to public keys.
    mux.Lock()
    publicKey, ok := keyMap[kid]
    mux.Unlock()
    if !ok {
        return nil, fmt.Errorf("%w: could not find a matching KID in the map of keys", ErrKID)
    }

    return publicKey, nil
}

现在,您可以在解析JWT时使用创建的keyFunc函数作为变量。

代码语言:javascript
运行
复制
// Parse the JWT.
token, err := jwt.Parse(myToken, keyFunc)
if err != nil {
    log.Fatalf("Failed to parse JWT.\nError: %s\n", err.Error())
}

// Confirm the JWT is valid.
if !token.Valid {
    log.Fatalln("JWT failed validation.")
}

// TODO Proceed with authentic JWT.

其他JWT公钥格式

RFC 7517也可以描述JWT的公钥。这个RFC描述了一个JSON密钥(JWK)和JSON密钥集(JWK)。一些身份提供者,如凯克雅克亚马逊科尼图(AWS)通过HTTPS端点提供这些服务。

下面是一个JWK示例:

代码语言:javascript
运行
复制
{
  "keys": [
    {
      "kid": "zXew0UJ1h6Q4CCcd_9wxMzvcp5cEBifH0KWrCz2Kyxc",
      "kty": "RSA",
      "alg": "PS256",
      "use": "sig",
      "n": "wqS81x6fItPUdh1OWCT8p3AuLYgFlpmg61WXp6sp1pVijoyF29GOSaD9xE-vLtegX-5h0BnP7va0bwsOAPdh6SdeVslEifNGHCtID0xNFqHNWcXSt4eLfQKAPFUq0TsEO-8P1QHRq6yeG8JAFaxakkaagLFuV8Vd_21PGJFWhvJodJLhX_-Ym9L8XUpIPps_mQriMUOWDe-5DWjHnDtfV7mgaOxbBvVo3wj8V2Lmo5Li4HabT4MEzeJ6e9IdFo2kj_44Yy9osX-PMPtu8BQz_onPgf0wjrVWt349Rj6OkS8RxlNGYeuIxYZr0TOhP5F-yEPhSXDsKdVTwPf7zAAaKQ",
      "e": "AQAB",
      "x5c": [
        "MIICmzCCAYMCBgF4HR7HNDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwMzEwMTcwOTE5WhcNMzEwMzEwMTcxMDU5WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCpLzXHp8i09R2HU5YJPyncC4tiAWWmaDrVZenqynWlWKOjIXb0Y5JoP3ET68u16Bf7mHQGc/u9rRvCw4A92HpJ15WyUSJ80YcK0gPTE0Woc1ZxdK3h4t9AoA8VSrROwQ77w/VAdGrrJ4bwkAVrFqSRpqAsW5XxV3/bU8YkVaG8mh0kuFf/5ib0vxdSkg+mz+ZCuIxQ5YN77kNaMecO19XuaBo7FsG9WjfCPxXYuajkuLgdptPgwTN4np70h0WjaSP/jhjL2ixf48w+27wFDP+ic+B/TCOtVa3fj1GPo6RLxHGU0Zh64jFhmvRM6E/kX7IQ+FJcOwp1VPA9/vMABopAgMBAAEwDQYJKoZIhvcNAQELBQADggEBALILq1Z4oQNJZEUt24VZcvknsWtQtvPxl3JNcBQgDR5/IMgl5VndRZ9OT56KUqrR5xRsWiCvh5Lgv4fUEzAAo9ToiPLub1SKP063zWrvfgi3YZ19bty0iXFm7l2cpQ3ejFV7WpcdLJE0lapFdPLo6QaRdgNu/1p4vbYg7zSK1fQ0OY5b3ajhAx/bhWlrN685owRbO5/r4rUOa6oo9l4Qn7jUxKUx4rcoe7zUM7qrpOPqKvn0DBp3n1/+9pOZXCjIfZGvYwP5NhzBDCkRzaXcJHlOqWzMBzyovVrzVmUilBcj+EsTYJs0gVXKzduX5zO6YWhFs23lu7AijdkxTY65YM0="
      ],
      "x5t": "IYIeevIT57t8ppUejM42Bqx6f3I",
      "x5t#S256": "TuOrBy2NcTlFSWuZ8Kh8W8AjQagb4fnfP1SlKMO8-So"
    }
  ]
}

在上面的示例中,可以仅从*rsa.PublicKeye JSON属性创建一个JSON。它的kid属性可以用于在前面的部分中描述的keyMap中创建一个条目。

实际上,我以前遇到过这个用例,并创建了一个Go包,只用于使用JWK和创建jwt.Keyfunc。如果这适合您的用例,请查看这里的存储库:github.com/MicahParks/keyfunc

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25848215

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档