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

时间:2018-08-09 09:37:12

标签: c# asp.net-mvc .net-core asp.net-identity identityserver4

我正在尝试让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应用中包含的“启动”(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中注销(第一点)。

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

3 个答案:

答案 0 :(得分:3)

Account/Logout页面中,该页面位于脚手架的ASP.NET Core身份验证代码中的Areas/Identity/Account/Logout.cshtml.cs下,其中有一个OnGet处理程序,如下所示:

public void OnGet() { }

因为这使用的是ASP.NET Core Razor页面,所以所有要做的就是呈现相应的Logout.cshtml页面。在您的示例中,当您在MVC应用程序中点击Logout时,它将清除自己的Cookie,然后将您传递到IS4应用程序(特别是OnGet)。由于此OnGet处理程序为空,因此它实际上并没有做任何事情,也肯定不会使您退出IS4应用。

如果您查看OnPost内的Logout.cshtml.cs处理程序,就会发现它看起来像这样:

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

SignOutAsync的调用完全符合其建议:它使您退出IS4本身。但是,在您当前的工作流程中,未调用此OnPost处理程序。如前所述,在MVC应用程序中使用OnGet时会间接调用Logout处理程序。

现在,如果您在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。删除该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选项,只是为了使此答案短一点。除此之外,假设您在ASP.NET Core Identity世界中,它仅使用_signInManager进行注销。

我鼓励您探索Quickstart.UI实现中的完整源代码,以支持ShowLogoutPromptreturnUrl等-在这里,如果没有编写书。

答案 1 :(得分:1)

可以实现简单的注销功能,如下所示:

import math
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


nu = []
b = [0.1 + i / 100 for i in range(0, 510)]
d = 0.1


for beta in b:
    nu.append( math.sqrt(1+(2*d*beta)**2)/ math.sqrt((1-beta**2)**2+(2*d*beta)**2))

#turned d into array of length 510 with 0.1 for each value
d = np.ones(510)*0.1


fig = plt.figure()
ax = Axes3D(fig)
ax.plot(b, d, nu)
plt.show()

答案 2 :(得分:0)

我遇到了同样的问题,我已经解决了,因此决定将我的答案放在这里,供其他人查看。

解决方案:IdentityServer4内部已经存在快速入门项目逻辑,并可以根据用户需要对其进行配置。

  1. 打开SolutionName / Quickstart / Account / AccountOptions.cs
  2. ShowLogoutPrompt 设置为false
  3. AutomaticRedirectAfterSignOut 设置为true

Example

我希望这会有所帮助,祝你好运。