我正在try 使用GO实现访问令牌验证.但我在网上看到的示例似乎只使用TOKEN_SECRET进行了验证.然而,我习惯于使用Java Spring编程,在那里我不需要使用TOKEN_SECRET.我只提供了jwk-set-uri,它会判断有效性(自动安全过滤器等),我知道它会与OAuth服务器通信并进行验证.

Go中是否没有lib通过向OAuth服务器发出请求来判断令牌是否有效?

是的,我知道我可以通过向OAuth服务器的用户信息端点发出请求来手动完成此操作:

http://localhost:8080/auth/realms/<your_realm>/protocol/openid-connect/userinfo

(使用密钥授权将令牌包含在标头中)

但我不知道这是否完全符合标准.

做这件事的正确方法是什么?

推荐答案

简而言之:使用go-oidc

长长的答案: 首先,让我们了解一下Spring Security如何自动验证作为JWT的访问令牌.按照惯例,如果您在application.yaml配置文件中定义OAuth 2.0或OIDC客户端属性,则Spring将自动连接安全筛选器链中的一个筛选器,该筛选器从Keyshaak获取jwk-set,这是一组公钥,对应于Keyloak用来签署令牌的私钥.此过滤器将应用于所有受保护的路由,并且Spring将使用公钥来判断令牌的签名是否有效,并在适用的情况下进行其他判断(受众、超时等)

那么,我们如何在Go中做到这一点呢?我们可以编写一个简单的中间件,它接受jwk-set并使用它来验证令牌.您需要解码令牌,确认isssub声明以确保它来自可信的发行者和目标受众,判断它是否尚未过期,然后判断alg声明以查看使用了哪种签名算法,从jwk-set中 Select 相应的公钥并判断签名是否有效.

func JWTMiddleware(jwkSet map[string]*rsa.PublicKey) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            authHeader := r.Header.Get("Authorization")
            if authHeader == "" {
                http.Error(w, "Authorization header is required", http.StatusUnauthorized)
                return
            }

            tokenString := strings.TrimPrefix(authHeader, "Bearer ")
            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"])
                }

                alg := token.Method.Alg()
                publicKey, ok := jwkSet[alg]
                if !ok {
                    return nil, fmt.Errorf("no key found for signing method: %v", alg)
                }
                return publicKey, nil
            })
            // Other checks, ISS, Aud, Expirey etc ...

            if err != nil || !token.Valid {
                http.Error(w, "Invalid token", http.StatusUnauthorized)
                return
            }

            next.ServeHTTP(w, r)
        })
    }
}

Go相关问答推荐

VS代码,Golang格式顽固的情况与switch / case

从Kafka到Clickhouse的实时消费数据

如何在VSCode中为特定的.go文件创建调试配置?

go-chi: 接受带有反斜杠的 url 路径参数

如何找到一个空闲的TPM句柄来保存新的密钥对对象?

xml.Unmarshal 不支持的类型 struct

如何判断范围内的字段?

使用 unsafe.Pointer 将 struct point直接转换为另一个 struct 是否安全?

为什么互斥量比 golang 中的通道慢?

golang 中的可变参数函数

读取非UTF8编码的文件内容并正确打印出来

获取切片元素的地址是否意味着 Go 中元素的副本?

如何将一片十六进制字节转换为整数

级联调用泛型函数时的泛型类型推断

具有近似约束的函数值导致的实例化失败

Go 使用 struct 作为接口而不实现所有方法

函数参数的判断顺序是什么?

如何使用 httputil.ReverseProxy 设置 X-Forwarded-For

Golang 与 Cassandra db 使用 docker-compose:cannot connect (gocql)

如果在调用 http.Get(url) 时发生错误,我们是否需要关闭响应对象?