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 客户端的主要内容,如果未能解决你的问题,请参考以下文章