You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Golang验证Keycloak生成的JSON Token?密钥类型错误排查

问题

我尝试用Golang验证Keycloak生成的JSON Token时,遇到错误:Error parsing or validating token: key is of invalid type。以下是我的代码:

package main

import (
    "fmt"

    "github.com/dgrijalva/jwt-go"
)

func main() {
    // example token to validate
    tokenString := "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6MXdOWU9YVFRpd2RhQjJyS1NqNElrUG1YTDJHbXVTOVFRNERJeXpWbWlrIn0.eyJleHAiOjE2ODIxMTQ3MjgsImlhdCI6MTY4MjExNDQyOCwianRpIjoiZDE5NGZlMmEtZDUyOC00YjhhLWI3OWItODEwNDMxNWExNzIxIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9BcnFncmlmbyIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJmOWExMTYxYi00YzhiLTRmM2MtODViYy0xYmM1MDE0MWFmNTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ3ZWItYXBwIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIvKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hcnFncmlmbyIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJ3ZWItYXBwIjp7InJvbGVzIjpbInVtYV9wcm90ZWN0aW9uIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJjbGllbnRJZCI6IndlYi1hcHAiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImNsaWVudEhvc3QiOiIxNzIuMTcuMC4xIiwicHJlZmVycmVkX3VzZXJuYW1lIjoic2VydmljZS1hY2NvdW50LXdlYi1hcHAiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.vqFWx5mMESEMww8E5t1J8ZmoCw1R9qv1qlgaYaG7FQcd8B_sN223cDYMoqJF5y-Xv9zaJ094fUmyDtJHv-ZTkxw3R9AtjG0cCjqMxgBn1X2irlNYEmR5ZX73YXDUxY6XuABLyTGdh00bEcaUIyFR1Pver2UDjMf2okcV1FgEd0Z_94j4pjqtcY0nbsWIKnLoVoor7QV6ytWRpMG25DvrSVxciaOpogOHlUhaWtTfMz-mvfFg64i_S6rIuT84APnVe6weAuj92YS6bUzBif_gcgNeMdLrJChxWdPMK9G5mDAgLOqUv-X5fPOw1arigInV0nCJmKV7LG6Yc1UlDHdmiA"

    // define the secret key used to sign the token
    secretKey := []byte("P184S3h7Ugjl56l31qeJ4FKvmBB4iikc")

    // parse the token and validate it
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        // validate the signing algorithm
        if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }

        // return the secret key
        return secretKey, nil
    })
    // check if there was an error parsing or validating the token
    if err != nil {
        fmt.Println("Error parsing or validating token:", err)
        return
    }

    // check if the token is valid
    if !token.Valid {
        fmt.Println("Invalid token")
        return
    }

    // print the claims of the token
    claims := token.Claims.(jwt.MapClaims)
    fmt.Println("Claims:", claims)
}

已知该Token签名算法为RS256,我已尝试从Keycloak Realm获取公钥,但仍无法解决问题,请问哪里操作有误?

解决方案

你的核心问题是RS256是RSA非对称加密算法,验证时必须使用RSA公钥(*rsa.PublicKey类型),而你代码里返回的是字节数组格式的对称密钥,类型不匹配。以下是修正步骤:

  • 获取正确的Keycloak公钥:从Keycloak Realm的「Keys」页面复制完整的PEM格式公钥(开头为-----BEGIN PUBLIC KEY-----,结尾为-----END PUBLIC KEY-----)。
  • 解析公钥为合法类型:使用jwt.ParseRSAPublicKeyFromPEM方法将PEM格式的公钥解析成*rsa.PublicKey类型。
  • 替换验证回调的返回值:在jwt.Parse的回调函数中返回解析后的公钥,而非字节数组。

修正后的代码示例:

package main

import (
    "fmt"
    "github.com/dgrijalva/jwt-go"
)

func main() {
    tokenString := "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6MXdOWU9YVFRpd2RhQjJyS1NqNElrUG1YTDJHbXVTOVFRNERJeXpWbWlrIn0.eyJleHAiOjE2ODIxMTQ3MjgsImlhdCI6MTY4MjExNDQyOCwianRpIjoiZDE5NGZlMmEtZDUyOC00YjhhLWI3OWItODEwNDMxNWExNzIxIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9BcnFncmlmbyIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJmOWExMTYxYi00YzhiLTRmM2MtODViYy0xYmM1MDE0MWFmNTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ3ZWItYXBwIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIvKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hcnFncmlmbyIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJ3ZWItYXBwIjp7InJvbGVzIjpbInVtYV9wcm90ZWN0aW9uIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJjbGllbnRJZCI6IndlYi1hcHAiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImNsaWVudEhvc3QiOiIxNzIuMTcuMC4xIiwicHJlZmVycmVkX3VzZXJuYW1lIjoic2VydmljZS1hY2NvdW50LXdlYi1hcHAiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.vqFWx5mMESEMww8E5t1J8ZmoCw1R9qv1qlgaYaG7FQcd8B_sN223cDYMoqJF5y-Xv9zaJ094fUmyDtJHv-ZTkxw3R9AtjG0cCjqMxgBn1X2irlNYEmR5ZX73YXDUxY6XuABLyTGdh00bEcaUIyFR1Pver2UDjMf2okcV1FgEd0Z_94j4pjqtcY0nbsWIKnLoVoor7QV6ytWRpMG25DvrSVxciaOpogOHlUhaWtTfMz-mvfFg64i_S6rIuT84APnVe6weAuj92YS6bUzBif_gcgNeMdLrJChxWdPMK9G5mDAgLOqUv-X5fPOw1arigInV0nCJmKV7LG6Yc1UlDHdmiA"

    // 替换成你从Keycloak获取的完整PEM格式公钥
    keycloakPublicKey := []byte(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzc13NWYOXQTiwdaB2rKSj4IkPmXL2GmuS9QQ4DIyzVmWik
... 此处为完整公钥内容 ...
-----END PUBLIC KEY-----`)

    // 解析RSA公钥
    publicKey, err := jwt.ParseRSAPublicKeyFromPEM(keycloakPublicKey)
    if err != nil {
        fmt.Println("Failed to parse public key:", err)
        return
    }

    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        // 返回解析后的RSA公钥
        return publicKey, nil
    })

    if err != nil {
        fmt.Println("Error parsing or validating token:", err)
        return
    }

    if !token.Valid {
        fmt.Println("Invalid token")
        return
    }

    claims := token.Claims.(jwt.MapClaims)
    fmt.Println("Claims:", claims)
}

额外提示:github.com/dgrijalva/jwt-go已停止维护,建议迁移到官方维护的github.com/golang-jwt/jwt/v4,API逻辑基本一致,但版本更稳定。

内容的提问来源于stack exchange,提问作者Stephany Henrique

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 20:24:53