新文章 网摘 文章 随笔 日记

将单点登录引入现有的ASP.NET MVC应用程序

如果它们都是新的应用程序,那么为公司的一组业务应用程序实施单点登录并不困难,特别是如果您使用WS-Federation和Identity Server(例如Thinktecture)。如果它是新应用程序和现有应用程序的组合,那么如果您首先了解整个技术并了解其工作原理,它将有助于解决所有问题。Jarek分享了他的经验。

介绍

我目前正在从事一个项目,该项目需要我们将现有的ASP.NET MVC应用程序与后台和前台的许多新系统集成在一起。用户希望他们都可以像一个集成应用程序一样一起工作,并且一个关键的要求是所有Web系统都应该有一个单一的登录(SSO)。

用户将需要能够在任何或所有这些应用程序的页面之间导航,而无需繁琐的重复身份验证。

在本文中,我想分享一些我们遇到的问题,以及在设计和实现基于.NET WIF,Thinktecture Identity Server v2和WS-Federation协议的单点登录时解决这些问题的方式。该集成项目。

单点登录原则

WS-Federation,SAML2P,OpenID和OAuth都提供了执行单点登录(SSO)的方式,并具有类似的一般原理。如果您了解其中一个是如何工作的,则很容易掌握其他任何一个。

下图展示了基本SSO背后的通用总体思想-至少就我们谈论Web应用程序而言:

2178-federa1.png

该图介绍了使用被动重定向机制的Web应用程序的SSO过程的基本步骤。术语“被动”反映了这样一个事实,即过程中涉及的应用程序不会彼此直接通信,而是依赖于浏览器的重定向以及标准的HTTP GET和POST消息。当依赖方应用程序直接(例如通过Web服务)与身份提供者进行对话以验证用户的身份并获取相关的安全令牌时,我们可以称为“活动SSO”:当我们具有“较厚”的依赖关系时,需要这种直接通信各方(例如常规Windows应用程序)。通过一些努力,我们也可以在Web应用程序中使用“主动SSO”,但这是“被动重定向”路线,这是我所知道的Web SSO标准的基础,

在显示的图表上,我们有4个主要元素:

  • 用户
  • 两个Web应用程序:A和B。
  • 身份提供程序(从技术上讲,它也是一个Web应用程序,即它接受常规的HTTP请求)。

两个应用程序(A&B)均配置为使用单个身份提供程序-因此,当用户登录其中一个应用程序时,他(或她)应该已经意识到他(或她)已经登录到第二个应用程序(因此:单点登录)。

为了获得这种感知,需要执行以下步骤:

  1. 用户从应用程序A请求页面。
  2. 由于尚未在应用程序A中对用户进行身份验证,因此将用户重定向到身份提供者。
  3. 由于尚未在身份提供者中对用户进行身份验证,因此身份提供者会将用户重定向到身份提供者的登录页面。
  4. 用户输入凭据,然后在身份提供者中进行身份验证。
  5. 身份提供者创建身份验证令牌并将身份验证令牌发送到应用程序A。
  6. 一旦应用程序A使用接收到的身份验证令牌对用户进行身份验证,最初请求的页面A将返回给用户。
  7. 到目前为止,一切都很好,但是单个应用程序还没有真正的SSO,因此现在用户从应用程序B请求页面B。
  8. 由于尚未在应用程序B中对用户进行身份验证,因此将用户重定向到身份提供者。
  9. 由于已在身份提供者中对用户进行身份验证(在步骤4中),身份提供者将生成身份验证令牌并将其发送到应用程序B。
  10. 用户在应用程序B中获得身份验证,并且可以将页面B返回给用户。

这提供了在各种SSO标准中非常常见的高级序列。

在提供单一登录的所有可能方式中,我将在本文中重点介绍WS-Federation和Thinktecture Identity Server v2。我还将仅提及与我们的解决方案相关的WS-Federation功能。如果您对更多细节感兴趣,可以在MSDN上阅读有关WS-Federation的很好的介绍

从许多相互信任的独立身份服务/提供程序的意义上来说,本文中提到的项目不需要全面的信任联合:所有系统都托管在一个域中,我们只希望拥有一个身份提供者,因此这里没有信任问题。WS-Federation的伟大之处在于它的支持是Windows Identity Foundation(WIF)的一部分,WIF从.NET 4.5开始,已经内置在.NET Framework中。因此,至少在源代码中,在.NET应用程序中使用WS-Federation并没有明显的开销。一旦您知道如何解决它,这将相当容易。

应用领域和要求

该项目有四个主要组成部分:

  1. 现有的前台应用程序由几个模块组成:这些模块协同工作,以允许用户搜索,读取和管理客户信息。
  2. 现有应用程序,管理控制台,其中包括管理前台应用程序用户对菜单和可见链接等组件的访问权限。
  3. 一个新的后台系统将负责存储和处理主要通过现有前台应用程序访问的信息(我将在后面详细说明),它将是唯一存储用户详细信息和凭据的地方。后台系统将由两个主要元素组成:一个面向真实用户的网站和一个针对应用程序的SOAP服务。
  4. 一个新的报告系统,这将扩展现有前台应用程序的数据表示功能。实际上,该系统是另一个Web应用程序。

所有应用程序/系统都将托管在我们客户的环境中。除了管理控制台,它们都可以从Internet和Intranet进行访问。管理控制台只能从Intranet访问。

我们可以更改现有的应用程序,前台和管理控制台,但是我们无权访问或控制后台系统或报告系统的源代码。可以将所有新系统(后台和报告系统)配置为使用WS-Federation协议进行身份验证,这与大多数Java系统(出于联盟目的而仅支持SAML协议)不同。

最初,现有应用程序,前台和管理控制台都使用了自己独立的ASP.NET成员资格提供程序。这意味着这些应用程序之间没有单点登录(SSO)。

我们的客户需要:

  • 在现有的前台应用程序中(通过IFrame)呈现新后台系统和报告系统的选定视图(页面)。
  • 引入单点登录机制,以避免在不同系统之间导航时重复登录。
  • 确保登录页面的样式与现有的前台应用程序一致。
  • 仅将用户的凭据存储在新的后台系统中。该系统将提供验证外部应用程序的用户名和密码所需的服务。
  • 保留每个系统的授权机制– SSO仅应用于身份验证。其原因是,授权要求在各个应用程序之间差异很大,并且没有明显的业务理由来证明在此领域进行更改的合理性。

考虑到所有这些,以下是预期的解决方案:

2178图片%204-febfe115-782f-4538-bc2b

WS联合会和Thinktecture进行救援

在我们的案例中,因为我们不需要完整的信任联合,所以我们只对WS-Federation的以下功能感兴趣:

  • 单一登录使用被动重定向到单个身份提供程序从多个Web应用程序打开在下一节中,我将更详细地解释被动重定向的工作原理以及为何将其称为“被动”。
  • 联合签出退出从任何“联合”应用程序发起的注销将导致用户在单个会话中从其登录的所有其他应用程序中注销。

此外,熟悉一些术语可能会变得很方便:

  • 信赖方–任何依靠外部服务来验证其用户身份的应用程序。示例中提到的每个应用程序(即前台,管理控制台,后台和报告系统)都是信赖方。基本上,WS-Federation中的依赖方具有与Service Provider在SAML协议中相同的含义。
  • 身份提供者–这是一个向依赖方提供身份的系统。
  • 领域–这是与独立的“安全领域相关的非常通用的术语在我们的例子中,它们是表示每个应用程序(包括身份提供者)的“ home” URL的简单字符串值。

现在,我们唯一缺少的是身份提供者。我们可以从头开始实现它,尤其是当我们不想让身份提供者存储和管理用户信息时,但是应该通过新的后台系统通过Web服务访问这些信息。毕竟,WIF拥有所有组成部分,可帮助提供协议和通信。

幸运的是,无需这样做,因为我们有一个非常不错的开源替代方案,即Thinktecture Identity Server(如果感兴趣,请查看其文档源代码)。我们决定使用版本2,因为它受到客户的青睐-当时版本3仍处于测试阶段。

Thinktecture Identity Server是一种轻量级的安全令牌服务(STS),用.NET 4.5,ASP.NET MVC 4,WCF和Web API编写–支持许多“流行的”安全协议,包括WS-Trust和WS-Federation 。不幸的是,缺点之一是缺乏对SAML 2协议的支持-它们非常流行,尤其是在Java世界中。就SAML而言,该产品支持SAML 1.1和SAML 2.0令牌,但是SAML令牌规范只是整个SAML规范的一小部分。

单点登录理论

一切准备就绪后,我们就可以开始研究解决方案了。在开始时,对所有这些元素如何相互作用有基本的了解是非常重要的。对于我们来说,这尤为重要,因为我们已经有两个可以运行的应用程序(前台和管理控制台),并且我们不想破坏任何东西。不幸的是,这可能是由于SSO令人惊讶的副作用所致,尽管最初情况似乎很明显,但后来更多。

让我们看一下该图(此图仍然简化,但这次是更技术性的),该图描述了当用户首次尝试访问Web应用程序页面时,具有被动重定向的SSO是如何工作的:

2178图片%201-54d72351-36b5-480a-8abb

这是发生的情况的描述:

  1. 用户的浏览器从Web应用程序请求页面。
  2. Web应用程序检查用户是否已经登录(从技术上讲,它检查是否存在身份验证cookie)。
  3. 由于没有cookie,因此WS-Federation身份验证模块将启动并将用户重定向到Identity Server(HTTP 30x代码)。
  4. 用户的浏览器通过从Identity Server请求资源来响应重定向(它仍然是用于被动重定向的“常规” HTTP GET)。
  5. Thinktecture Identity Server在这里用作常规ASP.NET应用程序,并检查用户是否已通过身份验证(即,具有用于Identity Server的有效身份验证cookie)。
  6. 如果尚未在Identity Server上对用户进行身份验证,则请求另一个重定向-这次是Identity Server的登录页面。在Thinktecture的Identity Server实现中,这是通过标准Forms身份验证强制实施的。
  7. 用户输入其凭据。他会一直停留在页面上,直到他做对为止–即使在最终用户帐户(例如,尝试次数过多)最终被阻止的情况下,也不会将有关“无效登录尝试”的信息返回到原始Web应用程序。向前推进该过程的唯一方法是提供有效的用户凭据。
  8. 当用户正确输入凭据或已经登录时,身份服务器将创建安全令牌。默认情况下,令牌由非常基本的信息组成,称为“声明”,例如用户名,身份验证日期和时间,但可以轻松扩展以包含其他信息,例如角色或电子邮件地址。创建后,令牌将使用HTTP POST方法(以及身份提供者的身份验证cookie)发送到原始Web应用程序。在此特定情况下,使用了SAML令牌,但也支持JWT(JSON Web令牌)格式。
  9. POST调用由Web应用程序中WSFederationAuthenticationModule处理创建了带有ClaimsIdentity对象的有效ClaimsPrincipal并将其设置为当前用户。此外,还会为Web应用程序创建一个“常规” ASP.NET身份验证cookie。完成后,将完成Web应用程序中的身份验证步骤,然后执行后续步骤。其中一个步骤是授权-它可能会使用从Identity返回的声明。提供程序,但我们保留了自定义角色提供程序的实现,该角色提供程序从外部服务加载用户角色。完成授权后,请求的页面最终将返回给用户(带有新的Web应用程序身份验证cookie)。

主要有两种变体:

  1. 在Web应用程序中已经对用户进行身份验证后,将不会发生什么特别的事情–标准的ASP.NET身份验证机制将呈现响应,并且不会执行与WS-Federation相关的功能。
  2. 如果用户尚未在Web应用程序中进行身份验证,但是已经在身份提供者中进行了身份验证(例如,因为在同一会话中使用具有相同身份提供者的另一个应用程序时已登录该用户)。在这种情况下,唯一的区别是在步骤5之后,我们跳到步骤8。

由于使用了重定向,因此对每个相关应用程序的托管位置(Identity Server或依赖方)没有限制。在我们的情况下,所有应用程序都将共享一个域地址,但这绝不是必需的。

这些重定向实际上是被动重定向机制的核心(被动,因为您的应用程序不直接与身份提供者对话,也不是身份提供者直接与依赖方“对话”-所有通信都是使用浏览器在用户的工作站)。如本文开头所述,您可以始终考虑使用主动身份验证,即客户端直接要求STS /身份提供者获取有效的身份验证令牌(通过Web Service / Web API)。

SSO的意外功能

使用SSO,您面临一个重要的问题,即单点登录将暴露现有应用程序中的身份验证弱点。每个应用程序都需要分别检查单个授权用户应看到的内容。身份提供者仅确认用户就是他们声称的身份。

这导致我们的项目出现问题。在将“常规”身份验证替换为“单一登录”,测试人员提出了一个错误,即不应该访问前台应用程序的管理控制台用户可以打开它。

这个问题当然不是由SSO本身引起的-它根源于前台应用程序的授权过程,但是我认为这是很普遍的情况,即在尚未为SSO配置的应用程序中,一些基本功能是适用于所有经过身份验证的用户。

引入SSO时,应非常小心地使用它。如果您不能完全控制身份提供者对谁进行身份验证,则尤其如此。身份验证仅告诉您有关用户身份的信息–您需要在每个应用程序中分别检查用户应看到的内容。

联合注销该怎么办?

在用户共享浏览器的地方,联合注销尤为重要。考虑以下一系列事件:

  1. 用户(我们称他为John)在浏览器中打开新会话,然后将Url输入到前台应用程序。
  2. 接下来,他被重定向到登录页面(请注意,该页面托管在Identity Server中,而不是前台应用程序本身),在那里他成功输入了凭据并被重定向到前台主页。
  3. 他在那里查找一位客户,然后选择“显示客户详细信息”选项。实际上,他被重定向到新的前台页面,在该页面中(在IFrame中)向他显示了后台系统的客户详细信息页面。
  4. John选择了另一个选项,即“过去12个月的客户表现”。在前台应用程序中还向他提供了另一个IFrame,这次它的内容将是报告系统中的客户绩效报告。
  5. John认为现在是家庭时间,因此他从前台应用程序注销,然后回家。他没有关闭浏览器,也没有关闭计算机–因为他想让计算机准备好供玛丽使用,后者进行了第二次轮班,并在几分钟后开始在同一工作站上工作。

当Mary在浏览器中输入任何报告系统网址时,会发生什么?如果没有联合注销,她将能够以John身份登录到此页面。

那么,联合注销如何防止这种情况发生呢?让我们分析一系列相同的事件,但是这次是在更技术层面上进行分析(第一列描述了该步骤,第二列指定了该步骤之后用户浏览器中的会话Cookie(仅与场景相关))。

步#

描述

会话cookie

1个

用户进入前台应用程序网址

2个

身份验证后,用户将重定向到请求的前台页面

  • 身份服务器\身份验证。曲奇饼
  • 前台\身份验证。曲奇饼

3

向用户显示后台页面以及客户详细信息

  • 身份服务器\身份验证。曲奇饼
  • 前台\身份验证。曲奇饼
  • 后台\身份验证。曲奇饼

4

向用户显示报告系统中的报告

  • 身份服务器\身份验证。曲奇饼
  • 前台\身份验证。曲奇饼
  • 后台\身份验证。曲奇饼
  • 报告系统\身份验证。曲奇饼

5

用户从系统注销

如果您不知道为什么要添加新的cookie,请回顾一下“单点登录理论”部分。在上述每个步骤中,都会执行以下操作:

  • 重定向到Thinktecture Identity Server。
  • 检查用户身份验证状态。
  • 使用身份验证令牌从身份服务器到原始应用程序进行POST,并为特定应用程序创建身份验证cookie,无论我们输入应用程序边界的方式如何(直接通过从其他应用程序或从IFrame重定向)。

但是,当用户注销时,怎么可能从浏览器中删除所有cookie –毕竟(通常)没有站点可以删除任何其他站点的cookie?实际上,该机制在其简单性方面非常聪明-每个应用程序都删除其自己的cookie-在这里显示了如何:

2178-federa4.png

事实证明,Thinktecture Identity Server使用另一个cookie只是为了存储有关使用它来验证其用户身份的每个依赖方的信息。因此,当用户要注销时,将执行以下步骤:

  • 应用程序(依赖方),从该用户启动注销调用FederatedSignOut()的方法WSFederation一个uthentication中号odule在其MVC controller.This导致用户的浏览器的静态类被重定向到Thinktecture Identity Server的注销页面。
  • 当Thinktecture Identity Server退出页面返回到浏览器时,它:
  • 删除Thinktecture身份验证cookie。
  • 包含许多隐藏的IFrame,每个为其生成身份验证令牌的依赖方一个。每个IFrame的source属性等于以参数wa = wsignoutcleanup1.0扩展的依赖方领域Url 每个应用程序的WS-Federation http模块都可以识别此参数-该模块将添加到响应指令中以删除其身份验证cookie。

仅此而已。

请注意,与“单点登录”情况类似,我们需要考虑到我们无法控制将在何处启动退出。这意味着,如果(例如,在前台应用程序中)在“注销”控制器操作中有一些清理代码,则只有在注销在后台启动后才执行。从管理控制台启动注销后,如果需要,您仍将从前台应用程序中注销-但是不会执行前台清理代码。为了避免这种情况,您不应该依赖控制器的操作来处理注销清理–您应该切换到FederatedAuthentication.WSFederationAuthenticationModule.SignedOut事件(通常将此事件与代码绑定的最佳位置是Global.asaxApplication_Start方法)。

会话和滑动过期

通常,尤其是在企业应用程序中,我们不希望用户无限期地登录到我们的系统中。这意味着我们将使用会话cookie而不是永久cookie进行身份验证。此外,出于安全原因,会自动注销闲置时间超过可配置时间(例如30或60分钟后)的用户。

在常规的ASP.NET应用程序中,我们通过简单地在身份验证cookie上配置滑动到期来做到这一点。如果有多个应用程序连接到单个身份提供程序,则情况会有些复杂。

我们基本上有(至少)两个选择:

  • 滑动到期时间“每个应用程序”。
  • 滑动到期时间“每个身份提供者”。

在第一个选项(“每个应用程序滑动到期”)中,我们可以为每个应用程序和身份提供者设置相同时间的身份验证会话的持续时间(例如60分钟)。然后,每个应用程序将通过在每个请求上检查Identity Provider返回其令牌时创建的身份验证cookie并在必要时对其进行延长,来自行管理其用户会话的“滑动”。这当然是可行的(使用FederatedAuthentication.SessionAuthenticationModule类方法),但是它并不是特别优雅。

这种方法存在一个问题,因为更改会话持续时间的应用程序并未在所有应用程序和身份提供者中都这样做。这将引起问题,如下所示:

  1. 用户登录到应用程序A(通过身份提供程序)。此时,用户的身份验证Cookie设置为在接下来的60分钟内有效。
  2. 闲置15分钟后,用户打开应用程序B(配置为使用相同的身份提供程序)。身份提供者和应用程序B中的用户会话均被设置为在接下来的60分钟内有效。应用程序A中的用户会话将在接下来的45分钟内有效(因为有15分钟的空闲时间)。
  3. 在接下来的75分钟内,用户将在应用程序B上进行一些操作(因此其Cookie会不断重置为在接下来的60分钟内有效)。同时,身份提供程序和应用程序A中的用户会话均已到期。
  4. 接下来,用户执行一个操作,将该用户从应用程序B的页面重定向到应用程序A的页面。由于应用程序A中的用户会话已经到期,浏览器将用户重定向到身份提供程序。由于身份提供者中的用户会话也已到期,身份提供者将显示其登录页面。

如我们所见,尽管使用了通用的Identity Provider,但在该用例中实际上并没有SSO。并且仍在登录到应用程序B中,用户需要再次登录到应用程序A。

解决此问题的一种方法是将身份提供者的会话生存期设置为比单个应用程序中的会话要长得多(例如8或24小时)。但是,这仍然不能解决问题,而只是使其发生的可能性较小。它引入了一个新问题:除非用户使用前面描述的联合注销基础结构明确从系统注销,否则他/他可能会在很长时间内保持登录身份提供者(即可以访问该提供者的所有依赖方)的身份。

为了避免这些问题,我们可以使用“每个身份提供者的过期滑动时间”选项,如下图所示:

2178图片%201-46192095-afbb-4500-9b4e

为了“每个身份提供者的滑动到期时间”工作时,我们必须以特定方式配置Identity Server。

  1. 每个依赖方应用程序的身份验证cookie生存期(在上图中称为“本地cookie”)始终设置为固定的持续时间(因此那里没有滑动过期时间)。使用Thinktecture Identity Server v2配置中的依赖方使用tokenLifeTime参数配置此值
  2. 身份验证仅在身份提供者侧“滑动”(使用图中提到的SsoCookie)。此cookie的生存期由Thinktecture Identity Server v2常规配置中ssoDuration参数控制
  3. 为了使滑动到期起作用,ssoDuration应该始终长于tokenLifeTime(请参见下面的示例以获取更多说明)。

使用以下(简化的)参数示例可以更容易地对此进行描述:ssoDuration = 1 hour(即,身份提供商应用程序中用户的会话持续时间)和tokenLifeTime = 30分钟(即依赖方应用程序中用户的会话时长):

  1. 在12:00对用户进行身份验证,因此身份提供者将创建一个ssoCookie,其到期时间设置为13:00,而依赖方应用程序的令牌设置为在12:30到期。依赖方应用程序创建其自己的身份验证cookie,该身份验证cookie设置为在12:30到期。
  2. 在12:30之前对依赖方应用程序的所有用户请求都在本地进行了身份验证(即未与身份提供者进行通信)。
  3. 12:35的用户请求无法在本地进行身份验证(依赖方应用程序cookie已过期),因此向身份提供者发出了身份验证请求。由于身份提供者的ssoCookie仍然有效,因此无法将用户重定向到登录页面,并且新的身份验证令牌将返回到依赖方应用程序(设置为在30分钟内13:05过期)。同时(在Identity Provider中)使用ssoDuration值1小时延长了ssoCookie的生存期,因此新的ssoCookie到期时间设置为14:05。
  4. 如果下一次用户操作在14:06发生,则应用程序身份验证cookie和Identity Provider的ssoCookie都将过期–然后,用户将被重定向到登录页面。

上面的描述被简化。实际上,还有许多其他因素:

  1. 时钟时间差异:实际上,SSO通常是在不同位置的许多服务器之间建立的,因此我们不能确保所有时钟都同步。为了适应这一点,maximumClockSkew参数确定应接受不同服务器之间的最大可接受时间差。默认情况下,在WIF的WS-Federation中,此参数等于5分钟。该参数的副作用(特别是在单台计算机上同时测试身份提供者和依赖方时)的副作用是,如果将ssoDuration(或tokenLifeTime)设置为1小时,则实际上创建的令牌将在1小时5分钟后过期。这似乎是一个很小的差异,但是如果(出于测试目的)您设置了tokenLifeTime到1分钟,并且在6分钟之前还没有过期,您开始想知道出什么问题了(通常使用完全不同的词:-))。此参数的值可以在web.config的<system.identityModel>部分中进行配置
  2. 滑动到期“漏洞”
    1. 为了使ssoCookie发挥滑动作用,需要在依赖方应用程序和身份提供者之间进行身份验证请求-只要“本地”应用程序身份验证cookie有效,就不会执行此类请求。这意味着,例如,具有ssoDuration = 60分钟和tokenLifeTime= 30分钟,如果用户在前30分钟内(即在原始身份验证cookie的固定生命期内)与依赖方应用程序进行了密集的交互,然后在接下来的30分钟内喝咖啡,则身份提供者中的用户会话将在他/她登录后60分钟到期,而不是他/她在依赖方应用程序中的最后一个操作之后60分钟到期。理论上,可以通过使用较短的tokenLifeTime值(控制依赖方应用程序中身份验证cookie的生存期)来消除这种影响,但是随着它增加对身份提供者的调用频率,将导致一些性能开销。
    2. 更复杂的是,在Thinktecture的Identity Server v2实现中,ssoCookie到期扩展不会在ssoCookie生命周期的前半部分进行

请注意,默认情况下,Thinktecture Identity Server中的滑动到期未处于活动状态v2实现–您需要Thinktecture.IdentityServer.sln解决方案OnPremise \ WebSite项目Global.asax.cs添加以下代码

 

 

JavaScript和WS-Federation的限制

到目前为止,我们讨论的所有机制(即具有用于单点登录和联合注销的被动重定向的SSO)在讨论常规HTTP请求时都可以正常工作,无论请求是指整个浏览器还是IFrame。请注意,只要身份提供者和依赖方在同一个域中,IFrame部分仅部分正确,因为默认情况下,如果某些浏览器的源域与“宿主”页面URL中的源域不同,则某些浏览器将拒绝来自IFrame的cookie。 。

当我们在应用程序中广泛使用Java Script和AJAX请求时,情况变得有些棘手。这里的主要问题是在WS-Federation中,身份验证响应是使用HTTP POST从身份提供者发送到依赖方的,而我找不到任何更改它的方法。

让我们考虑以下示例:

  1. 用户通过身份提供者登录(依赖方)应用程序A。
  2. 系统A被写为“ SPA”(Web单页应用程序)–加载初始页面(MVC视图)后,所有进一步的通信都在此初始页面中托管的JavaScript代码与许多Web API方法之间进行。
  3. 系统A中的身份验证cookie设置为30分钟的固定生存期,而身份提供者的身份验证cookie配置为在60分钟后过期,但具有到期时间。
  4. 31分钟后,应用程序A中的一种JavaScript方法(getCustomers)调用了Web API方法(GetCustomers)。这是发生了什么:
    1. getCustomers
      http://superdomain.com/ApplicationA/WebAPI/GetCustomers发起请求
    2. 应用程序A中的认证cookie已经过期,因此该请求使用代码302重定向到WS-Federation Identity Provider http://evenbetterdomain.com/IdentityProvider/issue。
    3. 响应302代码由浏览器引擎自动处理,请求到达身份提供者。由于仍在身份提供者中对用户进行身份验证,因此将生成新的身份验证令牌,并将对应用程序A的POST响应返回给调用方。
    4. 而且…什么都没发生–请求在这里结束– –特别是没有向用户提供任何客户数据。

为什么?主要问题在于POST响应-没有“重定向到POST”响应代码之类的东西。步骤c中返回的身份提供者。上面的代码实际上是常规的(代码:200)HTML响应,带有嵌入的JavaScript方法来执行POST。如果使用Thinktecture Identity Server v2,则生成的HTML如下所示:

 

 

如果将这样的结果返回给浏览器,则文档末尾window.setTimeout方法将导致立即提交所提供的表单。该表单将发布到依赖方URL(在本例中为http://superdomain.com/ApplicationA/)。除其他值(在隐藏的输入字段中设置)外,还有一个上下文(输入id =““ wctx ”),该上下文指示浏览器在处理安全令牌时应将用户重定向到何处–在这种情况下,它是原始的WebAPI方法的请求URL:/ ApplicationA / WebAPI / GetCustomers。

问题在于,当这样的文档以字符串形式返回给JavaScript调用者时,文档中包含的脚本不会执行。

实际上,当不再在身份提供者中对用户进行身份验证并且需要重定向到登录页面时,会发生非常相似的行为-在这种情况下,呈现的登录页面HTML将返回给调用者-再次不是这种情况。预计。

实际上,当我们使用“每个应用程序的滑动到期时间”。在这种情况下(即,当我们在依赖方方面滑动到期时),在密集的用户交互过程中身份验证将过期的机会非常小,因此没有什么可以阻止我们退出标准解决方案,在该解决方案中,我们可以更改action / WebAPI的方式当未经身份验证的用户尝试调用该方法时,该方法将起作用。只要我们调用自己的应用程序,对于设计为由Java脚本代码调用的所有操作,我们都会更改其行为以返回HTTP代码401(未找到)而不是302(在ASP.NET MVC应用程序中,它可以通过将标准AuthorizeAttribute替换为使用HttpRequestBase.IsAjaxRequest()的自定义扩展版本确定其行为的方法)。当然,我们需要在JavaScript中处理此响应-但是由于“幕后”一事无成-我们拥有完全控制权,并且我们可能会指示JavaScript代码始终退回到完全重定向到当前页面的状态,只要返回了代码401 。对于SSO,这意味着用户将被重定向到身份提供商的登录页面,或者将自动生成身份验证cookie,并且该用户将停留在当前页面上。在任何情况下,JavaScript调用都不会成功,需要由用户再次启动。

但是,一个更为复杂的问题与“每个身份提供者的滑动到期时间”有关。在这种情况下,本地依赖方身份验证cookie将在每个固定时间段(例如,每30分钟)到期,因此对于用户来说,每隔30分钟中断他们在系统上所做的任何工作将是非常不愉快的体验。

我真的不知道有什么优雅的解决方案可以解决这个问题,但是我还远不是JavaScript专家,因此,我非常感谢在此方面的任何建议。

我能够提出一个解决方案的初稿,该解决方案虽然似乎可行,但绝非值得推荐的解决方案。

可以将其概括为几个步骤:

  1. 在用于处理来自WebAPI方法的JSON响应的JavaScript回调方法中,我们需要检测它是哪种响应:
    1. 有效的JSON文档。
    2. 来自身份提供者的HTML响应(带有POST的身份验证令牌)。
    3. 任何其他HTML响应。
  2. 如果它是JSON,则将其视为有效响应,因此我们需要做的所有事情-即应用程序继续其正常流程(场景在此处结束)。
  3. 如果它是来自身份提供者的HTML响应(带有到POST的身份验证令牌),我们会做一些(丑陋的)“魔术”:
    1. 我们将响应加载到隐藏的IFRAME。
    2. 我们等待IFRAME完成加载。届时应完成嵌入式JavaScript代码以及将创建新身份验证cookie的POST。在这种情况下,IFRAME将被重定向到最初请求的URL –即WebAPI方法。
    3. 我们检查这个隐藏的IFRAME的内容,如果这是有效的JSON响应,请获取其值并转到步骤2.如果不是,请转到步骤4。
  4. 如果是任何其他HTML响应(或者通常是前面提到的任何其他响应),则将强制刷新当前页面的整个页面。
    如果无效响应是由过期的身份验证引起的,这将导致重定向到身份提供者;或者仅刷新当前页面(如果这是由其他问题引起的)。如果添加了一些自定义错误消息,则可能是合理的。

我能够编写一些代码来证明这种方法确实可以工作-因为代码远非优雅,我真的不想在这里包含它,但是如果有人感兴趣的话-请告诉我,我会很高兴与您分享。

概括

在本文中,我尝试与您分享我将WS-Federation和Thinktecture Identity Server v2作为身份提供程序的一组现有ASP.NET MVC应用程序从常规身份验证切换到单一登录(SSO)的经验。

该主题涉及面很广,因此我不想涉及太多细节,尤其是与Thinktecture Identity Server v2和依赖方应用程序的自定义,安装和配置有关。我个人认为,在您的应用程序中开始SSO实现时,最重要的是首先了解基础技术和依赖项的概况。如果没有这种广泛的了解,您可能很容易就迷失在细节上,而不知道为什么有些东西无法按您预期的那样工作-但是,如果您了解全局,那么您可以选择最适合您的那些工具和协议。

如果对Single Sign-on有任何兴趣,我将很高兴继续阅读一些更深入的技术文章,重点放在选定的问题上。无论如何,我希望您在本文中至少为您的项目找到了一些有趣和有用的信息。

 
https://www.red-gate.com/simple-talk/dotnet/asp-net/introducing-single-sign-on-to-an-existing-asp-net-mvc-application/
posted @ 2021-04-16 16:17  岭南春  阅读(143)  评论(0)    收藏  举报