首页 文章

为什么最终用户必须注销两次?

提问于
浏览
7

我试图让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项目进行身份验证;授权等

我不得不做出以下改变:

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中注销(第一点) .

为什么最终用户必须注销两次?

2 回答

  • 3

    简单的注销功能可能如下:

    private readonly SignInManager<IdentityUser> _signInManager;
            private readonly ILogger<LogoutModel> _logger;
            private readonly IIdentityServerInteractionService _interaction;
            public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger,
                IIdentityServerInteractionService interaction)
            {
                _signInManager = signInManager;
                _logger = logger;
                _interaction = interaction;
            }
    
            public async Task<IActionResult> OnGet(string logoutId)
            {
                return await OnPost(logoutId);
            }
    
            public async Task<IActionResult> 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);
            }
    
  • 1

    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 处理程序是空的,所以's not really doing anything and it'肯定不会让你退出IS4应用程序 .

    如果你看一下 Logout.cshtml.cs 里面的 OnPost 处理程序,你会发现它看起来像这样:

    public async Task<IActionResult> OnPost(string returnUrl = null)
    {
        await _signInManager.SignOutAsync();
        // ...
    }
    

    这个对 SignOutAsync 的调用完全符合它的建议:它会让你退出IS4本身 . 但是,在当前工作流程中,未调用此 OnPost 处理程序 . 正如我已经提到的那样,当您在MVC应用程序中使用 Logout 时,会间接调用 OnGet 处理程序 .

    现在,如果您查看Quickstart.UI项目中IS4注销的控制器/操作实现,您将看到它基本上将 GET 请求传递给 POST 请求 . 这是代码,删除了注释:

    [HttpGet]
    public async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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实现中探索完整的源代码,以便支持 ShowLogoutPromptreturnUrl 等 - 如果不写书,我不可能在这里做 .

相关问题