如何在.NET项目中实现Postman式Keycloak授权流程?
.NET 对接 Keycloak 实现授权流程解决方案
一、控制台应用:交互式获取 Access Token
用Microsoft.Identity.Client(MSAL)实现和Postman一致的交互式授权流程,自动弹出浏览器引导登录:
- 安装NuGet包:
Microsoft.Identity.Client - 代码示例:
using Microsoft.Identity.Client; var clientId = "你的Keycloak客户端ID"; var realmName = "你的Keycloak Realm名"; var authority = $"https://你的Keycloak地址/realms/{realmName}"; var redirectUri = "http://localhost:5000/callback"; // 需提前在Keycloak客户端配置中添加此地址 var app = PublicClientApplicationBuilder .Create(clientId) .WithAuthority(authority) .WithRedirectUri(redirectUri) .Build(); var scopes = new[] { "openid", "profile", "email" }; // 根据业务需求添加对应Scope try { var result = await app.AcquireTokenInteractive(scopes) .WithPrompt(Prompt.SelectAccount) .ExecuteAsync(); Console.WriteLine($"Access Token: {result.AccessToken}"); } catch (MsalException ex) { Console.WriteLine($"获取Token失败: {ex.Message}"); }
二、.NET API:两种授权场景
场景1:保护API,仅允许携带合法Keycloak Token的请求访问
- 安装NuGet包:
Microsoft.AspNetCore.Authentication.JwtBearer - Program.cs配置:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = $"https://你的Keycloak地址/realms/{realmName}"; options.Audience = "你的API客户端ID"; // Keycloak中配置的API客户端ID options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true }; }); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapGet("/api/protected", () => "这是受Keycloak保护的API内容") .RequireAuthorization(); app.Run();
场景2:API自身调用其他受Keycloak保护的服务(获取Token)
用服务账户模式获取Token,无需用户交互:
using Microsoft.Identity.Client; var clientId = "API客户端ID"; var clientSecret = "API客户端密钥"; // Keycloak客户端开启"服务账户"后生成 var authority = $"https://你的Keycloak地址/realms/{realmName}"; var targetScope = "目标服务的Scope"; var app = ConfidentialClientApplicationBuilder .Create(clientId) .WithClientSecret(clientSecret) .WithAuthority(authority) .Build(); var result = await app.AcquireTokenForClient(new[] { targetScope }) .ExecuteAsync(); var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken); var response = await httpClient.GetAsync("https://目标服务地址/api/xxx"); var content = await response.Content.ReadAsStringAsync();
三、Razor WebApp:完整登录流程(修复未完成功能)
用ASP.NET Core内置的OpenID Connect中间件自动处理登录跳转、回调、Token存储,替代手动处理HTML的方式:
- 安装NuGet包:
Microsoft.AspNetCore.Authentication.OpenIdConnect、Microsoft.AspNetCore.Authentication.Cookies - Program.cs配置:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { options.Authority = $"https://你的Keycloak地址/realms/{realmName}"; options.ClientId = "WebApp客户端ID"; options.ClientSecret = "WebApp客户端密钥"; // 保密客户端需配置,公开客户端可省略 options.ResponseType = "code"; // 采用授权码流程,和Postman一致 options.SaveTokens = true; // 将Token保存到Cookie options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); options.GetClaimsFromUserInfoEndpoint = true; options.CallbackPath = "/signin-oidc"; // 需在Keycloak客户端配置中添加此回调地址 }); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
- 登录/退出页面实现:
- 在Index.cshtml添加登录入口:
@page @using Microsoft.AspNetCore.Authentication <div> @if (User.Identity.IsAuthenticated) { <p>欢迎, @User.Identity.Name!</p> <form method="post" asp-page="/Account/Logout"> <button type="submit">退出登录</button> </form> } else { <a asp-page="/Account/Login">登录</a> } </div>
- 创建
Account/Login.cshtml.cs:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace WebApp.Pages.Account; public class LoginModel : PageModel { public async Task<IActionResult> OnGetAsync(string returnUrl = null) { await HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = returnUrl ?? Url.Content("~/") }); return new EmptyResult(); } }
- 创建
Account/Logout.cshtml.cs:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace WebApp.Pages.Account; public class LogoutModel : PageModel { public async Task<IActionResult> OnPostAsync(string returnUrl = null) { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); return LocalRedirect(returnUrl ?? Url.Content("~/")); } }
内容的提问来源于stack exchange,提问作者L_Karapetti
相关产品推荐
相关产品推荐

