为什么最终用户必须注销两次?
我试图让IdentityServer4在新的.NET Core 2.1应用程序中运行(它在.NET Core 2.0应用程序中完美运行)。 我尝试过以下方法:
1)下载此项目,即IdentityServer4应用程序: https : //github.com/ghstahl/IdentityServer4-Asp.Net-2.1-Identity-Examples/tree/e0aeeff7e078aa082c8e16029dd2c220acc77d7b
2)使用Identity Server4应用程序下载此项目,即MVC应用程序: https : //github.com/IdentityServer/IdentityServer4.Samples/tree/dev/Quickstarts/6_AspNetIdentity/src/MvcClient 。
3)将两个项目添加到同一解决方案中。 MVC项目使用IdentityServer项目进行身份validation; 授权等
我不得不做出以下改变:
1)更改为IdentityServer应用程序中包含的Startup(AddIdentityServer现在接受参数):
services.AddIdentityServer(options => { options.UserInteraction.LoginUrl = "/Identity/Account/Login"; options.UserInteraction.LogoutUrl = "/Identity/Account/Logout"; })
2)配置IdentityServer应用程序以侦听端口5000并在身份服务器上禁用SSL。
除了注销工具外,一切都按预期开箱即用。 当我在MVC应用程序中单击注销时; 在MVC应用程序中调用以下代码:
public async Task Logout() { await HttpContext.SignOutAsync("Cookies"); await HttpContext.SignOutAsync("oidc"); }
然后,用户将重定向到IdentityServer应用程序中的Logout.cshtml。 但是,他们必须再次单击注销(在IdentityServer应用程序上)才能实际注销,即他们在MVC应用程序中单击注销(第二点),然后在IdentityServer中注销(第一点)。
为什么最终用户必须注销两次?
在Account/Logout
页面中,它位于您的脚手架ASP.NET核心标识代码中的Areas/Identity/Account/Logout.cshtml.cs
,有一个OnGet
处理程序,如下所示:
public void OnGet() { }
因为这是使用ASP.NET Core Razor Pages,所以这一切都是渲染相应的Logout.cshtml
页面。 在您的示例中,当您在MVC应用程序中点击Logout
时,它会清除自己的cookie,然后将您转到IS4应用程序(特别是OnGet
)。 因为这个OnGet
处理程序是空的,所以它并没有真正做任何事情,它肯定不会让你退出IS4应用程序。
如果你看一下OnPost
里面的OnPost
处理程序,你会发现它看起来像这样:
public async Task OnPost(string returnUrl = null) { await _signInManager.SignOutAsync(); // ... }
对SignOutAsync
这种调用完全符合它的建议:它会让你退出IS4本身。 但是,在当前工作流程中,不会调用此OnPost
处理程序。 当我在MVC应用程序中使用Logout
时,会间接调用OnGet
处理程序,正如我已经提到的那样。
现在,如果您在Quickstart.UI项目中查看IS4注销的控制器/操作实现,您将看到它本质上将GET
请求传递给POST
请求。 这是代码,删除了注释:
[HttpGet] public async Task Logout(string logoutId) { var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) return await Logout(vm); return View(vm); }
当注销时,有一个设置控制是否应首先提示用户确认他们是否要注销。 这主要是代码所处理的 – 如果不需要提示,它会直接将其传递给POST
请求处理程序。 这是POST
的代码片段:
[HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { await HttpContext.SignOutAsync(); // ... } // ... return View("LoggedOut", vm); }
这里重要的一行是对HttpContext.SignOutAsync
的调用 – 这最终会删除IS4用来保持登录的cookie。一旦删除,你就会退出IS4。 最终,这是您当前实施中缺少的内容。
在最简单的级别,您可以通过更新OnGet
来解决您的问题,如下所示:
public async Task OnGet() { if (User?.Identity.IsAuthenticated == true) { await _signInManager.SignOutAsync(); return RedirectToPage(); // A redirect ensures that the cookies has gone. } return Page(); }
这不支持我上面详述的ShowLogoutPrompt
选项,只是为了让这个答案更短一些。 除此之外,它只是使用_signInManager
来执行注销,因为您处于ASP.NET核心身份世界中。
我鼓励您从Quickstart.UI实现中探索完整的源代码,以支持ShowLogoutPrompt
, returnUrl
等 – 如果不returnUrl
,我不可能在这里做。
简单的注销function可能如下:
private readonly SignInManager _signInManager; private readonly ILogger _logger; private readonly IIdentityServerInteractionService _interaction; public LogoutModel(SignInManager signInManager, ILogger logger, IIdentityServerInteractionService interaction) { _signInManager = signInManager; _logger = logger; _interaction = interaction; } public async Task OnGet(string logoutId) { return await OnPost(logoutId); } public async Task OnPost(string logoutId) { await _signInManager.SignOutAsync(); _logger.LogInformation("User logged out."); var r = await _interaction.GetLogoutContextAsync(logoutId); if (r.PostLogoutRedirectUri == null) { return Redirect("/"); } return Redirect(r.PostLogoutRedirectUri); }
- System.Runtime.InteropServices.COMException(0x80080005):使用CLSID检索组件的COM类工厂
- 如何在asp.net代码中创建HTML字段集和图例?
- 如何将AcquireTokenAsync中收到的令牌与Active Directory一起存储
- 在Entity Framework Core中创建迁移时如何配置DbContext?
- dotnet核心中的RealProxy?
- 在.NET Core中引用时,从程序集文件加载的类型不等于相同的类型
- .NET Core 2.0 RSA PlatformNotSupportedException
- entity framework核心SelectMany然后包括
- 在ASP.NET Core Web API中上载文件和JSON
- AspNetCore.Identity无法使用自定义用户/角色实现
- 如何将相同的列添加到EF Core中的所有实体?