ASP.NET Core 的 Keycloak 客户端

Posted

技术标签:

【中文标题】ASP.NET Core 的 Keycloak 客户端【英文标题】:Keycloak client for ASP.NET Core 【发布时间】:2017-06-02 22:14:31 【问题描述】:

是否有任何现有的用于 Asp.net Core 的 Keycloak 客户端? I have found a NuGet package for .net 但它不适用于 Core。您对如何轻松与此安全服务器集成(或使用任何其他替代方案)有任何想法吗?

【问题讨论】:

你有没有找到这方面的资源? 尝试使用 Microsoft.AspNetCore.Authentication.OpenIdConnect 中的 UseOpenIdConnectAuthentication 并为 Keycloak 填充 OpenIdConnectOptions(权限:server+"auth/realms/"+realm, ClientId, ClientSecret)。 @mikes 您知道您建议使用 AspNetCore 和 keycloak 的此配置的在线示例吗?只是出于好奇,您为什么不在下面的答案中提供答案而不是评论?想知道 SO...的区别... @Talisker 只是没想到。 ;) 答案现在可用。 这个库对这里有帮助吗 - github.com/lvermeulen/Keycloak.Net 【参考方案1】:

对我们有用的是在 Startup.cs 中设置这些东西(它是基于 cookie 的身份验证):

public void Configure(...)

    (...)
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    
        AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
        AutomaticAuthenticate = true,
        CookieHttpOnly = true,
        CookieSecure = CookieSecurePolicy.SameAsRequest
    );

    app.UseOpenIdConnectAuthentication(CreateOpenIdConnectOptions(_customConfig));
    (...)

并设置选项:

private OpenIdConnectOptions CreateOpenIdConnectOptions(CustomConfigurationFile configuration)

    var options = new OpenIdConnectOptions
    
        AuthenticationScheme = "oidc",
        SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
        Authority = configuration.ServerAddress + "/auth/realms/" + configuration.Realm,
        RequireHttpsMetadata = true,
        PostLogoutRedirectUri = configuration.SystemAddress,
        ClientId = configuration.ClientId,
        ClientSecret = configuration.ClientSecret,
        ResponseType = OpenIdConnectResponseType.Code,
        GetClaimsFromUserInfoEndpoint = true,
        SaveTokens = true
    ;
    options.Scope.Clear();
    options.Scope.Add("openid");
    return options;

【讨论】:

此解决方案是否允许您使用标准授权属性,例如 [Autorize(Roles="MyKeycloakRole")] ?换句话说,KeyCloak 定义的角色是通过 OpenIdConnectAuthentication 自动提取的吗?欢呼 @Talisker 您找到解决方案/答案了吗?否则 keycloak 和你的服务是强耦合的。【参考方案2】:

我今天玩了一点。最直接的方式也是使用 OpenId 标准。

在 Startup.cs 中我使用了 OpenIdConnect 身份验证:

    public void Configure(...)
     (...)
         app.UseCookieAuthentication(new CookieAuthenticationOptions
        
            AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
            AutomaticAuthenticate = true,
            CookieHttpOnly = true,
            CookieSecure = CookieSecurePolicy.SameAsRequest
        );
        app.UseOpenIdConnectAuthentication(CreateKeycloakOpenIdConnectOptions());`(...)
 `

OpenIdConnectOptions 方法:

private OpenIdConnectOptions CreateKeycloakOpenIdConnectOptions()
    
        var options = new OpenIdConnectOptions
        
            AuthenticationScheme = "oidc",
            SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
            Authority = Configuration["Authentication:KeycloakAuthentication:ServerAddress"]+"/auth/realms/"+ Configuration["Authentication:KeycloakAuthentication:Realm"],
            RequireHttpsMetadata = false, //only in development
            PostLogoutRedirectUri = Configuration["Authentication:KeycloakAuthentication:PostLogoutRedirectUri"],
            ClientId = Configuration["Authentication:KeycloakAuthentication:ClientId"],
            ClientSecret = Configuration["Authentication:KeycloakAuthentication:ClientSecret"],
            ResponseType = OpenIdConnectResponseType.Code,
            GetClaimsFromUserInfoEndpoint = true,
            SaveTokens = true

        ;
        options.Scope.Add("openid");
        return options;
    

在 appsettings.json 中为 Keycloak 添加配置:


  (...),
  "Authentication": 
    "KeycloakAuthentication": 
      "ServerAddress": "http://localhost:8180",
      "Realm": "demo",
      "PostLogoutRedirectUri": "http://localhost:57630/",
      "ClientId": "KeycloakASPNETCore",
      "ClientSecret": "secret-get-it-in-keycloakConsole-client-credentials"
    
  

Keycloak 客户端配置如下:

Client settings, I've added 'accounting' role for test, I added mapper 'member_of' of type 'User Client Role' for roles so that roles are added in the claims

如果我想按角色授权用户,我会这样做:

在 ConfigureServices 方法中添加authorization by claims:

public void ConfigureServices(IServiceCollection services)
    
        (...)

        services.AddAuthorization(options =>
        
            options.AddPolicy("Accounting", policy =>
            policy.RequireClaim("member_of", "[accounting]")); //this claim value is an array. Any suggestions how to extract just single role? This still works.
        );
    

我在 ValuesController(默认 Web API 模板)中编辑了 get 方法:

[Authorize(Policy = "Accounting")]
[Route("api/[controller]")]
public class ValuesController : Controller

    // GET api/values        
    [HttpGet]
    public Dictionary<string,string> Get()
    
        var userPrinciple = User as ClaimsPrincipal;
        var claims = new Dictionary<string, string>();

        foreach (var claim in userPrinciple.Claims)
        
            var key = claim.Type;
            var value = claim.Value;

            claims.Add(key, value);
        


        return claims;
    

如果我使用具有会计角色的用户或在具有会计角色的组中登录,它应该在地址 localhost:57630/api/values 上显示我的用户声明。

我希望这对你有用。

编辑:.NET Core 2 大家好!我的应用程序的工作方式发生了很大变化,我还没有完全测试 .NET Core 2,但您仍然可以尝试在 ConfigureServices 中像这样连接到 Keycloak:

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            

                options.Authority = Configuration["Authentication:KeycloakAuthentication:ServerAddress"] + "/auth/realms/" + Configuration["Authentication:KeycloakAuthentication:Realm"];
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                
                    ValidAudiences = new string[]  "curl", "financeApplication", "accountingApplication", "swagger"
                ;
                options.RequireHttpsMetadata = false; //for test only!
                options.SaveToken = true;
                options.Validate();

            );

在配置中:

app.UseAuthentication();

您可以稍后使用 IHttpContextAccessor httpContextAccessor 访问您的令牌,例如:

public KeycloakAuthorizationRequirementHandler(IConfiguration config,
            IHttpContextAccessor httpContextAccessor,
            IMemoryCache memoryCache)
        
            _config = config;
            _httpContextAccessor = httpContextAccessor;
            _memoryCache = memoryCache;
        

//获取访问令牌

var accessToken = _httpContextAccessor.HttpContext.GetTokenAsync("access_token");

_httpContextAccessor.HttpContext.Items["username"] = username;

告诉我进展如何。

【讨论】:

非常有用且完整的答案!我唯一无法工作的是映射器member_of,它不在用户声明列表中。你介意分享你用来创建它的设置吗?谢谢。 您可以通过单击客户端“映射器”选项卡中的“创建”来创建“member_of”。 “映射器类型”应设置为“用户客户端角色”和“添加到 ID 令牌” - 开启。注销用户后,更改应在用户声明中可见。 @gimly 如果用户拥有多个角色,您可以使用基于策略的自定义授权进行授权,您可以在其中检查每个用户的角色。 感谢您的帮助,我在索赔中看不到它的原因是我没有给出索赔名称。现在它可以正常工作了。 好的,实际上这在 .net core 2 中似乎已经过时了。获得更新会很棒! :)【参考方案3】:

如果您想将标准 .Net 角色映射与 Keycloak 客户端角色一起使用,请进行如下设置:

Startup.cs:

    services.AddAuthorization(options =>
    
        options.AddPolicy("Users", policy =>
        policy.RequireRole("Users"));
    );

    services.AddAuthentication(options =>
    
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    )
    .AddCookie()
    .AddOpenIdConnect(options =>
    
        options.Authority = Configuration["Authentication:oidc:Authority"]
        options.ClientId = Configuration["Authentication:oidc:ClientId"];
        options.ClientSecret = Configuration["Authentication:oidc:ClientSecret"];
        options.RequireHttpsMetadata = false;
        options.GetClaimsFromUserInfoEndpoint = true;
        options.SaveTokens = true;
        options.RemoteSignOutPath = "/SignOut";
        options.SignedOutRedirectUri = "Redirect-here";
        options.ResponseType = "code";

    );

appsettings.json:

  "Authentication": 
    "oidc": 
      "Authority":"http://your-keycloak-server/auth/realms/your-realm",
      "ClientId":"Your-Client-Name",
      "ClientSecret":"Your-client-secret"
    
  

Keycloak 客户端设置:

创建新的令牌映射器 Mapper-Values(输入您自己的客户名称)

现在您可以使用标准授权角色语句将您的 Keycloak 客户端角色应用于您的 ASP.NET 项目:

[Authorize(Roles = "Users")]

【讨论】:

只是为了明确一点:对于 /Signout 我应该使用我的 keycloak-realm 的 logout-endpoint,不是吗?或者你是否在 /signout 下创​​建了一个资源,它在内部调用它并创建一个注销页面或其他东西? 两者都行,这取决于您的要求。如果您需要拦截注销过程的任何部分,比如让用户留在您的 UI 上而不是重定向到 keycloak,那么您将创建自己的注销并实现 keycloak API 以注销。否则,keycloak 注销端点就足够了。 另外要明确一点,上面的配置会检查keycloak,自动获取signout路径,并在你的应用中分配给“/SignOut”。这是 options.RemoteSignOutPath 配置设置。 嘿,我在注销时遇到问题。该应用程序会自动重新对用户进行身份验证。我打电话给:do! ctx.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme)do! ctx.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme)(我使用 F# 和 Saturn)。知道有什么问题吗?退出后有什么特别需要做的吗?【参考方案4】:

我们能否通过 .net core 5+ 获得有关此问题的最新答案?我最近安装了 keycloak 版本 13.0.0 并且它的工作允许我使用单点登录和几个应用程序。现在出于真正的原因,我安装了 keycloak,用于 webapi 身份验证。根据上面的答案,我已经安装了 Microsoft.AspNetCore.Authentication.OpenIdConnect 并一直在努力让它在 webapi 端和客户端上都能正常工作。

【讨论】:

你可以尝试使用这个库github.com/lvermeulen/Keycloak.Net

以上是关于ASP.NET Core 的 Keycloak 客户端的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.NET Core 中转储解析的 JWT 令牌?

ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT)

ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT)

使用 Keycloak 作为 ASP.Net 应用程序的单一登录解决方案

ASP.NET Core (.NET Core) and ASP.NET Core (.NET Framework)区别

Asp.Net core (Full .Net framework) vs Asp.Net core (.Net Core) 性能