我有一个需要认证的Angular客户端。此客户端托管在.NetCore SPA中。现在使用IdentityServer4,我已经设置了IDP以对客户端进行身份验证。
一切正常,可用于登录。客户端在这里自动重定向到IDP项目进行登录。输入凭据后,它将重定向回客户端应用程序。
但是,对于注销它不起作用。在作为.netcore中的SPA托管的Angular客户端上,我有一个注销按钮。此注销按钮事件将在同一客户端中调用API控制器。在此控制器中,有用于从httpContext注销的代码。
我的期望是,当在控制器中调用注销操作时,它将注销并重定向我回到IDP。积极的一点是,我已经在MVC客户端中实现了相同的功能(没有SPA和angular),并且可以通过将我重定向回IDP来注销。
另一个积极的看法是,在注销的角度客户端上,它实际上尝试注销,但是 CORS策略阻止了它。浏览器控制台中的错误消息将重定向URL作为错误消息的一部分。当我通过将其重定向到IDP并注销我单击该URL时,该URL有效。
我无法弄清楚必须添加哪些CORS策略才能使此重定向有效。我尝试将CORS添加到启动类中,但是没有用。请帮助
浏览器错误消息:
从原点“ https://localhost:44336/connect/endsession?post_logout_redirect_uri=https%3A%2F%2Flocalhost%3A44374%2Fsignout-callback-oidc&id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IlMwbFpqUi1QazItS0dLc2xxaFlQQ2ciLCJ0eXAiOiJKV1QifQ.eyJuYmYiOjE1NzkzNDc3NDcsImV4cCI6MTU3OTM0ODA0NywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMzYiLCJhdWQiOiJBY3Rpdml0eVRyYWNrZXJOZ0NsaWVudF9DbGllbnRJZCIsIm5vbmNlIjoiNjM3MTQ5NDQ1NDMyMDYwODM5LllUVmpaV1UyTURjdE5ERmhOeTAwTW1SbExXRm1NVGd0TWpKaVl6VTFOMlJoWkRkaE9EYzNPV1EwTm1VdFpEUmxaUzAwT1RNMkxUZ3hOV0l0T0RJeU5HWTVaR1l4T0RsaSIsImlhdCI6MTU3OTM0Nzc0NywiYXRfaGFzaCI6ImJwc3hLLThSU2Nwb2hKMnJPSlViQlEiLCJzX2hhc2giOiJ0M2R1RmljZDR1VTRTQlQ3S253cG1nIiwic2lkIjoiYTFIWnE5YXFMdnR1WHB6S1FUZUVvUSIsInN1YiI6ImQ4NjBlZmNhLTIyZDktNDdmZC04MjQ5LTc5MWJhNjFiMDdjNyIsImF1dGhfdGltZSI6MTU3OTM0Nzc0NywiaWRwIjoibG9jYWwiLCJhbXIiOlsicHdkIl19.bd35dk-lcolUxgoNAzzc4kKIORQIsmeSu5JaARpyqj1I6cv5P6LSHrcdw3YmZ80q_tF8WLi7ywIml-enEP4JAe-nbYw7gSlFt9qHtw5eSF37dMdBZq7UUXt6EoK29xs9lp6TyIB11pzgRZ8tPVAPw0Y8rNpGSGYtjfWjp7t4FdKthvUchAo_SNh6l40S5oV0Yo_YIWfHtjxM-nLZXia0YCvjNEQChmTmkzSMCIdGnVqawhIzQ_O7jv0c1T7kCwaF5YGyer3ZUyj1UM53JTBbbGpKDrDh2DV-kd4tvhoaLnWQAoUqCQ1Ofl_kHc8vffqE7RRPGmQLQYOM48186hIe0g&state=CfDJ8DRPXADjz9hKioMAFvg6DCP1P37ODZ4R81EV3uFXBpxiOLWoJY6GDEcbYNZzB--zZjv-Z94PSfMJkcoJhQcmHAvmM_9yKL9hPaGqmucpJrO_wv74Fj8bmdm8C7l_MJZ3VaNahF5Bqvi9tWFUikbr-HJ_uI0GiGX6qsj5mkrp8K4x&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.5.0.0”到“ https://localhost:44374/api/Authorization/Logout”(重定向自“ https://localhost:44374”)对XMLHttpRequest的访问已被CORS策略阻止:没有“访问控制允许来源” '标头出现在请求的资源上
以及API控制器注销代码:
[HttpGet]
public async Task Logout()
{
var httpclient = new HttpClient();
var disco = await httpclient.GetDiscoveryDocumentAsync("https://localhost:44336/");
// get the access token to revoke
var accessToken = await HttpContext
.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
if (!string.IsNullOrWhiteSpace(accessToken))
{
var revokeAccessTokenResponse =
await httpclient.RevokeTokenAsync(new TokenRevocationRequest
{
Address = disco.RevocationEndpoint,
ClientId = "App_ClientId",
ClientSecret = "someSecret",
Token = accessToken
});
if (revokeAccessTokenResponse.IsError)
{
throw new Exception("Problem encountered while revoking the access token."
, revokeAccessTokenResponse.Exception);
}
}
// Clears the local cookie ("Cookies" must match name from scheme)
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
具有整个客户端配置的Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<ActivityTrackerAPIHttpClient>();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = Configuration["IdentityServer:Authority"];
options.ClientId = Configuration["IdentityServer:ClientId"];
options.ClientSecret = Configuration["IdentityServer:ClientSecret"];
options.ResponseType = "code id_token";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("roles");
options.Scope.Add(Configuration["IdentityServer:ApiName"]);
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.Remove("amr");
options.ClaimActions.DeleteClaim("sid");
options.ClaimActions.DeleteClaim("idp");
options.ClaimActions.MapJsonKey("role", "role");
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = JwtClaimTypes.Role,
};
});
services.ConfigureLoggerService();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated)
{
await context.ChallengeAsync("oidc");
}
else
{
await next();
}
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
答案 0 :(得分:0)
当时我想出了一个临时解决方案。但是我意识到,在 angular 方面做客户端安全是理想的。
我的临时解决方案如下。
客户端的Startup.cs
options.Events = new Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents
{
OnRedirectToIdentityProviderForSignOut = (context) =>
{
var protocolMessage = context.ProtocolMessage;
var param = "";
foreach (var parameter in protocolMessage.Parameters)
{
param += $"{parameter.Key}={parameter.Value}$";
}
var url = $"{protocolMessage.IssuerAddress}?{param}x-client-SKU={protocolMessage.SkuTelemetryValue}&x-client-ver=5.5.0.0";
context.HttpContext.Session.SetString("LogoutUrl", url);
return Task.FromResult(0);
}
};
还通过在 ConfigureServices 方法中添加 services.AddSession();
然后在 Configure 方法中添加 app.UseSession();
来启用客户端 startup.cs 中的会话。
在控制器中
[HttpGet]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
var url = HttpContext.Session.GetString("LogoutUrl");
return Ok(url);
}