【问题标题】:Logout of MVC Application From Angular 5从 Angular 5 注销 MVC 应用程序
【发布时间】:2023-04-03 03:00:02
【问题描述】:

我们有一个基于 MVC 身份验证的 Angular 5 应用程序。该应用程序是从 Home/Index 操作提供的,一旦加载了应用程序,角度路由就会处理几乎所有事情。我们这样做主要是因为我们想使用 MVC 的身份验证过程(我们使用 Identity Server 4 作为我们的 Oath 系统)。

这很有效,但有一个例外:注销。当我们尝试注销时,应用程序似乎立即被重新授权并重新加载,而不是让我们返回到 Identity Server 登录页面。

最初,我们通过这段代码在我们的开发环境中取得了成功:

[HttpPost]
public async Task<IActionResult> Logout()
{
  foreach (string key in Request.Cookies.Keys)
  {
    Response.Cookies.Delete(key);
  }

  await HttpContext.SignOutAsync();

  return Ok();
}

但这是一个误报,因为我们所有的应用程序都在本地主机上运行,​​所以它们可以访问彼此的 cookie。因此,Identity Server cookie 与 Angular 应用程序的 cookie 一起被清除。

我们试图用这样的东西替换那个逻辑:

    public async Task Logout()
    {
        if (User?.Identity.IsAuthenticated == true)
        {
            // delete local authentication cookie
            await HttpContext.SignOutAsync("Cookies");
            await HttpContext.SignOutAsync("oidc");
        }
    }

但是,它在这两种环境中都没有注销(但是该代码适用于我们使用相同身份服务器的 MVC 应用程序之一)。

为了提供一些背景知识,我们的 Angular 应用程序的注销过程来自一个材质菜单,然后在调用注销函数之前,我们会在该菜单中提示用户他们是否真的想使用模式注销。这个过程的代码sn-ps:

注销按钮调用的方法:

public openDialog(): void {
    let dialogRef = this.dialog.open(ModalComponent, {
      width: '250px',
      data: { text: this.logoutText, ok: this.okText, cancel: this.cancelText }
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result !== undefined) {
        switch (result.data) {
          case 'ok':
            this.logout();
            break;
          case 'cancel':
            break;
          default:
            break;
        }
      }
   });
}

private logout(): void {
   this.sessionService.logOut();
}

会话服务:

public logOut(): void {
    this.auth.revokeToken();
    this.http.post(this.logoutUrl, undefined).subscribe(x => window.location.reload());
}

如前所述,以这种方式调用注销最终会导致页面刷新并且用户并未真正注销。我们可能缺少一些简单的东西,但到目前为止我所做的所有修补都没有带来任何成功。

编辑:

我们的角度路由相当简单(尽管路径阻止我们调用 home/logout 之类的东西):

{
  path: 'parts',
  component: PartsComponent,
  canActivate: [AuthGuard],
  runGuardsAndResolvers: 'always',
  data: { title: 'Parts' }
},
{
  path: '',
  redirectTo: 'parts',
  pathMatch: 'full'
},
{
  path: '**',
  redirectTo: 'parts'
}

我们的 MVC 路线也相当简单

    app.UseMvc(routes =>
    {
      routes.MapRoute(
       name: "default",
       template: "{controller=Home}/{action=Index}/{id?}");

      routes.MapRoute(
       "Sitemap",
       "sitemap.xml",
       new { controller = "Home", action = "SitemapXml" });

      routes.MapSpaFallbackRoute(
        name: "spa-fallback",
        defaults: new { controller = "Home", action = "Index" });
    });

我不确定我们如何能够以原来的角度路线轻松地路由到家/注销。我猜我们必须为它添加一条路线。我们曾经尝试过,但它从未正确路由。我正在尝试查找有关我们尝试过的内容的笔记。

【问题讨论】:

  • 当页面刷新时,用户是否注销?还是他仍然登录,但页面刚刚刷新?
  • @dAxx_ 它清除 Angular 应用程序的 cookie。刷新后地址栏中显示的 URL 是登录页面的 URL(包括重定向查询字符串),但它从不显示登录页面,只是再次重定向到 Angular 应用程序,就好像用户已经登录一样。
  • 您没有提供任何路由配置或守卫,但您使用其中任何一个吗?在您的 http.POST 中,您只需执行一个 window.loaction.reload(),这不是服务的工作,您应该将 Observable 返回给组件,并在那里订阅它。通过路由器执行到登录组件的导航。
  • @MarshallTigerus 您的 ajax 请求可能只清除客户端应用程序 cookie。要清除身份服务器 cookie,我认为您应该将用户重定向到注销页面。
  • 添加了更多信息。

标签: asp.net-core angular5 identityserver4 logout


【解决方案1】:

在您清除应用程序的本地 cookie 后,将用户发送到您的 Idsrv 的 end_session_endpoint。 (您显示的清除会话的代码应该可以工作,如果没有,我将在 startup.cs 中进行配置并调试以检查重定向是否真的删除了浏览器中的 cookie)。

例如https://demo.identityserver.io/.well-known/openid-configuration

在那里你看到了端点。这应该会删除 Idsrv 上的会话。根据您的设置,这可能会终止您在使用相同 Identity Server 实例的其他应用程序上的会话。

您可以在docs 中阅读更多相关信息。

我认为 Angular 路由不会干扰您的服务器端路由。只要您当然真的使用常规window.location.replace 重定向到该页面

当然,就像@Win 提到的那样,如果您使用它们,撤销 refresh_token 和 reference_tokens 将是一个很好的安全指南。

【讨论】:

  • 我找到了一个比这更简单的解决方案。我们已经在身份服务器上实现了注销视图/端点,所以我只是调用了它。这比我想象的要简单得多。但是,调用 end_session_endpoint 也有效
  • 你是说我需要从我的 Angular 应用程序中手动清除 cookie 吗?我想,既然是SignInAsync() 正在创建会话cookie,那么它应该是SignOutAsync() 删除它。更具体地说,我当前的问题是我将用户发送到 endsession 提供身份令牌,IDS4 将它们发送回我的控制器 logoutId。但是,使用它。除了客户的名字外,我在上下文中什么都没有。因此,cookie 没有被删除...
【解决方案2】:

我无法回答 Angular;我还在努力。但是,当用户在客户端注销时,客户端 Web 应用程序可能会要求 IDP 撤销访问令牌并刷新令牌。例如,在 ASP.Net Core -

using System;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

namespace MY_APP.Controllers
{
    public class AccountController : Controller
    {
        [HttpGet]
        public async Task<IActionResult> SignOut()
        {
            var discoveryClient = new DiscoveryClient("IDP_URL");
            var metaDataResponse = await discoveryClient.GetAsync();

            var revocationClient = new TokenRevocationClient(
                metaDataResponse.RevocationEndpoint, 
                "CLIENT_NAME",
                "CLIENT_SECRET");

            // revoke the access token
            string accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);

            if (!string.IsNullOrWhiteSpace(accessToken))
            {
                var revokeAccessTokenResponse = await revocationClient.RevokeAccessTokenAsync(accessToken);

                if (revokeAccessTokenResponse.IsError)
                    throw new Exception("Problem encountered while revoking the access token.", 
                        revokeAccessTokenResponse.Exception);
            }

            // revoke the refresh token
            string refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);

            if (!string.IsNullOrWhiteSpace(refreshToken))
            {
                var revokeRefreshTokenResponse = await revocationClient.RevokeAccessTokenAsync(refreshToken);

                if (revokeRefreshTokenResponse.IsError)
                    throw new Exception("Problem encountered while revoking the refresh token.",
                        revokeRefreshTokenResponse.Exception);
            }

            return SignOut(
                new AuthenticationProperties { RedirectUri = "CALL_BACK_URL" },
                CookieAuthenticationDefaults.AuthenticationScheme,
                OpenIdConnectDefaults.AuthenticationScheme);
        }
    }
}

【讨论】:

    猜你喜欢
    • 2019-10-01
    • 2014-08-01
    • 2013-08-19
    • 1970-01-01
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 2016-02-07
    • 1970-01-01
    相关资源
    最近更新 更多