首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Keycloak实现授权

如何使用Keycloak实现授权
EN

Stack Overflow用户
提问于 2020-12-18 12:53:28
回答 1查看 1.4K关注 0票数 0

我在Go中创建了一个REST API,这是一个必要的授权层,对于这一层,我尝试使用Keycloak。API将由第三方后端服务使用,有谁知道集成Go客户端和keycloak的工作流程,或者已经实现了吗?我想出了一个名为Gocloak的适配器,但在它的文档中没有任何用于此目的的示例。

EN

回答 1

Stack Overflow用户

发布于 2020-12-18 21:28:48

我有一个很有希望的好答案给你,因为我已经在专业和个人项目中做到了这一点。我给出了关于如何对请求进行身份验证的详细信息,并暗示授权,因为这是更特定于项目的。

你可以使用两种方法,如果你使用Golang + JWTs,我称之为“被动”和“主动”检查。Keycloak可以配置为将所有授权信息放入它签名的JWTs中。Golang代码将在对JWT进行身份验证后读取此授权信息。

在我开始之前,如果你正在使用Kubernetes + Istio,有一个non-Golang method。这将仅执行身份验证。如果它适合您的用例,那么它应该是理想的解决方案,因为它将涵盖名称空间,并且您不必为其编写代码。

被动检查

它不会每次都涉及到Keycloak服务器的网络活动。首先,您需要找到Keycloak应用程序的JWKS URL。这是official docs上的页面。您可以使用Ctrl + F来查看有关Keycloak + JWKS的详细信息。

在解析和验证令牌之后,您可以提取声明。然后,您可以提取授权所需的信息。

优点:每次有JWT时,Golang代码和Keycloak之间没有网络活动。

缺点:如果Keycloak撤销了某人的权限,Golang代码将继续授权请求,直到所有JWTs过期。(最长应为60秒或更短。)

如果您的安全需求允许,我将使用此方法。

我已经从我的原始答案更新了项目,以使用此方法。这是a link to that function

这是example from my package repo

代码语言:javascript
复制
package main

import (
    "log"
    "time"

    "github.com/dgrijalva/jwt-go"

    "github.com/MicahParks/keyfunc"
)

func main() {

    // Get the JWKS URL.
    //
    // This is a local Keycloak JWKS endpoint for the master realm.
    jwksURL := "http://localhost:8080/auth/realms/master/protocol/openid-connect/certs"

    // Create the keyfunc options. Refresh the JWKS every hour and log errors.
    refreshInterval := time.Hour
    options := keyfunc.Options{
        RefreshInterval: &refreshInterval,
        RefreshErrorHandler: func(err error) {
            log.Printf("There was an error with the jwt.KeyFunc\nError: %s", err.Error())
        },
    }

    // Create the JWKS from the resource at the given URL.
    jwks, err := keyfunc.Get(jwksURL, options)
    if err != nil {
        log.Fatalf("Failed to create JWKS from resource at the given URL.\nError: %s", err.Error())
    }

    // Get a JWT to parse.
    jwtB64 := "eyJhbGciOiJQUzM4NCIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMeDFGbWF5UDJZQnR4YXFTMVNLSlJKR2lYUktudzJvdjVXbVlJTUctQkxFIn0.eyJleHAiOjE2MTU0MDY5ODIsImlhdCI6MTYxNTQwNjkyMiwianRpIjoiMGY2NGJjYTktYjU4OC00MWFhLWFkNDEtMmFmZDM2OGRmNTFkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.Rxrq41AxbWKIQHWv-Tkb7rqwel3sKT_R_AGvn9mPIHqhw1m7nsQWcL9t2a_8MI2hCwgWtYdgTF1xxBNmb2IW3CZkML5nGfcRrFvNaBHd3UQEqbFKZgnIX29h5VoxekyiwFaGD-0RXL83jF7k39hytEzTatwoVjZ-frga0KFl-nLce3OwncRXVCGmxoFzUsyu9TQFS2Mm_p0AMX1y1MAX1JmLC3WFhH3BohhRqpzBtjSfs_f46nE1-HKjqZ1ERrAc2fmiVJjmG7sT702JRuuzrgUpHlMy2juBG4DkVcMlj4neJUmCD1vZyZBRggfaIxNkwUhHtmS2Cp9tOcwNu47tSg"

    // Parse the JWT.
    token, err := jwt.Parse(jwtB64, jwks.KeyFunc)
    if err != nil {
        log.Fatalf("Failed to parse the JWT.\nError: %s", err.Error())
    }

    // Check if the token is valid.
    if !token.Valid {
        log.Fatalf("The token is not valid.")
    }

    log.Println("The token is valid.")
}

主动检查

此方法将在每次JWT进入时与您的Keycloak服务器进行网络活动。它与Keycloak提供的REST端点通信,并且应该让您知道是否有任何JWTs在到期前被撤销。它使用gocloak project。示例auth*处理程序是我使用令人惊叹的goswagger project从我的swagger 2.0规范生成的代码。

优点:这里有一个例子。确定传入请求的JWT是否被撤销。

缺点:大量网络流量无法正确扩展。

所有这些GitHub链接都指向一个提交ID,就在我为一个个人项目进行身份验证之前。

为了获得工作所需的信息,您需要一个充当服务帐户的Keycloak客户端。客户端的Access Type应为confidentialService Accounts Enabled开关应为ON。确保使用底部的按钮方式保存客户端。

Keycloak docker-compose.yml

Keycloak export

Quickstart line

快速入门示例:

代码语言:javascript
复制
package configure

import (
    "errors"
    "strings"

    "go.uber.org/zap"

    "github.com/Nerzal/gocloak/v7"

    "github.com/MicahParks/terse-URL/models"
)

const (

    // headerPrefix is the prefix the JWT has in the "Authorization" header's value.
    headerPrefix = "Bearer "
)

var (

    // ErrJWTExpired indicates the JWT has expired.
    ErrJWTExpired = errors.New("JWT has expired")
)

type KeycloakInfo struct {
    BaseURL      string
    ClientID     string
    ClientSecret string
    Realm        string
}

func HandleAuth(keycloakInfo *KeycloakInfo, logger *zap.SugaredLogger) (authHandler func(headerValue string) (jwtInfo *models.JWTInfo, err error), err error) {

    // Create the Keycloak client.
    keycloak := gocloak.NewClient(keycloakInfo.BaseURL)

    // Create a context for logging into Keycloak.
    ctx, cancel := DefaultCtx()
    defer cancel()

    // Log into Keycloak with the given client.
    if _, err = keycloak.LoginClient(ctx, keycloakInfo.ClientID, keycloakInfo.ClientSecret, keycloakInfo.Realm); err != nil {
        return nil, err
    }

    // Create the authentication handler via a closure.
    return func(headerValue string) (jwtInfo *models.JWTInfo, err error) {

        // Strip the prefix from the header.
        headerValue = strings.TrimPrefix(headerValue, headerPrefix)

        // Create the JWT info structure that will be passed to the endpoints.
        jwtInfo = &models.JWTInfo{}

        // Create a context to authorize this JWT.
        ctx, cancel := DefaultCtx()
        defer cancel()

        // Check the JWT with Keycloak.
        var res *gocloak.RetrospecTokenResult
        if res, err = keycloak.RetrospectToken(ctx, headerValue, keycloakInfo.ClientID, keycloakInfo.ClientSecret, keycloakInfo.Realm); err != nil {
            logger.Errorw("Failed to retrospect token with Keycloak.",
                "error", err.Error(),
            )
            return nil, err
        }

        // Confirm the JWT is active.
        if !*res.Active {
            logger.Warnw("An inactive JWT was received.",
                "JWT", headerValue,
            )
            return nil, ErrJWTExpired
        }

        // TODO Populate the JWT information in the return value.

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

https://stackoverflow.com/questions/65351977

复制
相关文章

相似问题

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