谁能告诉我为什么以下(来自https://github.com/dgrijalva/jwt-go)示例不起作用?
token, err := jwt.Parse(myToken, func(token *jwt.Token) ([]byte, error) {
return myLookupKey(token.Header["kid"])
})
if err == nil && token.Valid {
deliverGoodness("!")
} else {
deliverUtterRejection(":(")
}
Run Code Online (Sandbox Code Playgroud)
我得到一个错误,说"不能使用func文字(类型func(*jwt.Token)([] byte,error))作为jwt.Karse参数中的类型jwt.Keyfunc"
我试图使用几个不同的jwt-go示例中的代码,但总是遇到同样的错误.
功能Parse
期望
type Keyfunc func(*Token) (interface{}, error)
Run Code Online (Sandbox Code Playgroud)
您需要返回interface{}
,而不是byte[]
在函数文字中.
(可能使用a byte.Buffer
来包装byte[]
,然后你可以像" 将任意Golang接口转换为字节数组 " 那样读取)
Gert Cuykens在第36期的评论中指出:提交e1571c8应该更新了这个例子.
像这个要点的其他例子也需要更新.
正如另一个答案中提到的,最新版本github.com/dgrijalva/jwt-go
是v3.2.0+incompatible
. 这些文档现在已经过时了,因为包的jwt.Keyfunc
函数现在具有以下签名:
type Keyfunc func (*Token) (interface{}, error)
Run Code Online (Sandbox Code Playgroud)
解析 JWT 时,此jwt
包还会对它们进行身份验证。验证 JWT 需要两件事。
这就是jwt.Keyfunc
适合的地方。 的返回值interface{}
稍后被断言为加密密钥。对于许多用例,这将是基于 RSA 或 ECDSA 的算法。这意味着返回类型通常是*ecdsa.PublicKey
或*rsa.PublicKey
。(也可以使用 HMAC 密钥,但我不会介绍该用例。)
如果您的公钥采用 PEM 格式,您可能有兴趣使用jwt
包中内置的函数:ParseECPublicKeyFromPEM
和ParseRSAPublicKeyFromPEM
。
如果您的公钥采用其他格式,您可能需要*ecdsa.PublicKey
构建*rsa.PublicKey
通过填充导出字段来
这是要填充的数据结构:
// PublicKey represents an ECDSA public key.
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
}
Run Code Online (Sandbox Code Playgroud)
嵌入elliptic.Curve
是通过使用crypto/elliptic
与其长度相关的函数创建的:
// 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
Run Code Online (Sandbox Code Playgroud)
对于 和X
,Y
这些都是*big.Int
s。如果您有这些整数的字节,请创建一个新的*big.Int
并使用该SetBytes
方法。
publicKey.X = big.NewInt(0).SetBytes(xCoordinate)
publicKey.Y = big.NewInt(0).SetBytes(yCoordinate)
Run Code Online (Sandbox Code Playgroud)
这是要填充的数据结构:
// A PublicKey represents the public part of an RSA key.
type PublicKey struct {
N *big.Int // modulus
E int // public exponent
}
Run Code Online (Sandbox Code Playgroud)
模数N
是a *big.Int
。如果您有该整数的字节,请创建一个新的*big.Int
并使用该
SetBytes
方法。
publicKey.N = big.NewInt(0).SetBytes(modulus)
Run Code Online (Sandbox Code Playgroud)
指数是一个常规整数。所以这应该是直接的,但是如果你有这个整数的字节,你可以创建一个像这样的整数:
publicKey.E = int(big.NewInt(0).SetBytes(exponent).Uint64())
Run Code Online (Sandbox Code Playgroud)
jwt.Keyfunc
现在公钥位于正确的数据结构中,是时候创建一个jwt.Keyfunc
. 如前所述,此函数的输入将为 a *jwt.Token
,输出将为 an*ecdsa.PublicKey
或 a
*rsa.PublicKey
,但键入为空接口:interface{}
或 an error
。
包含*jwt.Token
JWT 本身,但识别它与哪个公钥关联的最佳方法是kid
. 这kid
是 JWT 标头中包含的字符串值。阅读
kid
RFC 中有关该参数的信息。可以像这样从 JWT 读取它:
// 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)
}
Run Code Online (Sandbox Code Playgroud)
此时,输入为a kid
,输出为公钥。jwt.Keyfunc
此时a 最简单的实现
是一个从 a 读取的函数map[string]interface{}
,其中键string
是 a kid
,值是interface{}
是其公钥。
这是一个例子:
// 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
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以在解析 JWT 时使用创建的keyFunc
函数作为变量。
// 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.
Run Code Online (Sandbox Code Playgroud)
JWT 的公钥也可以通过RFC 7517进行描述。此 RFC 描述了 JSON Web 密钥 (JWK) 和 JSON Web 密钥集 (JWK)。一些身份提供商,例如Keycloak 或Amazon Cognito (AWS)通过 HTTPS 端点提供这些信息。
这是一个 JWK 示例:
{
"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"
}
]
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,*rsa.PublicKey
可以仅从
n
和e
JSON 属性创建 a。它的kid
属性可用于在keyMap
属性可用于创建前面部分中描述的
我之前实际上遇到过这个用例,并创建了一个 Go 包,只是为了使用 JWK 和创建
jwt.Keyfunc
. 如果这适合您的用例,请查看此处的存储库:github.com/MicahParks/keyfunc
。