如何在 Razor 页面中获取 ASP.NET 身份验证票证到期?

Posted

技术标签:

【中文标题】如何在 Razor 页面中获取 ASP.NET 身份验证票证到期?【英文标题】:How to get ASP.NET Identity authentication ticket expiry in Razor Page? 【发布时间】:2019-11-27 15:39:30 【问题描述】:

我将 identityserver4 与 ASP.NET Identity 结合使用,cookie 配置为 SlidingExpiration = true 和 ExpireTimeSpan = 20 分钟。我想在用户即将超时时向用户发出警告,因此我正在尝试访问 cookie 中的“.expiry”值。

到目前为止,我已经能够使用下面的 Razor 代码读取一个到期时间。 但是当用户刷新他们的票时,这无法读取正确的到期时间。根据Microsoft docsSlidingExpiration,如果我的用户在获得票证后刷新页面 10 分钟或更长时间(>= ExpireTimeSpan 的 50%),他们应该为我的用户提供新票证。它这样做很好,但是当它这样做时,下面的代码会提供旧的到期时间,直到用户第二次刷新页面!

@(DateTime.Parse(((await Context.AuthenticateAsync()).Properties.Items)[".expires"]))

我想知道的是如何在提供新票时生成的页面上获得正确的到期时间?

【问题讨论】:

【参考方案1】:

这里的问题是更新是在您的页面呈现之后发生的 - 新属性将仅在后续请求中可用。

一个选项可能是使用客户端代码来轮询最新的到期时间,但这会产生保持活动的效果,这可能不是您想要的。

为了缓解上述问题,您可以创建自己的 cookie 中间件实现(所有源代码都在 github 上)并自定义滑动过期逻辑(CheckForRefresh 和 RequestRefresh)。您还可以将新的到期时间添加到 HttpContext 中,从而使其更早地可用于您的控制器代码。

源码:https://github.com/aspnet/Security/tree/master/src/Microsoft.AspNetCore.Authentication.Cookies

您只需要创建自己的 CookieAuthenticationHandler 版本(少于 500 行代码)和自己的注册助手(查看 CookieExtensions):

public static AuthenticationBuilder AddCustomCookie(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<CookieAuthenticationOptions> configureOptions)

    builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureCookieAuthenticationOptions>());
    return builder.AddScheme<CookieAuthenticationOptions, CustomCookieAuthenticationHandler>(authenticationScheme, displayName, configureOptions);

【讨论】:

谢谢麦琪。我突然想到,直到页面呈现后才设置过期时间,我想编写一些临时自定义中间件来帮助确定设置过期时间的确切时间。我正在考虑编写一些只会更新过期时间的中间件。如果我可以在呈现页面之前在 cookie 中,或者在设置 cookie 到期后在呈现的页面中。它有一种 hacky 的感觉,但我认为它应该可以完成这项工作。在决定是否值得您的方法努力之前,我可能会先了解情况。想法? 我目前正在尝试您的方法,但遇到了一些非常早期的障碍。如果我创建自己的 CookieAuthenticationHandler,我将如何告诉 Asp.NET Identity 使用我的 CookieAuthenticationHandler?此外,我正在查看 CookieExtensions.cs 的代码,但对于如何使用它来创建我自己的注册帮助程序或在创建注册帮助程序后如何处理它几乎没有任何线索。 @Rob 基本上你会创建你自己的扩展方法,比如AddRobCookie(...),并将AddScheme... 调用改为使用RobCookieAuthenticationHandler 而不是CookieAuthenticationHandler。在您的 Startup 类中,您将使用新的扩展方法。 @Rob 添加了一个示例 谢谢@mackie,自从我上次发表评论以来,我已经了解了这么多,但不明白如何告诉 Asp.NET Identity 使用我的 CookieAuthenticationHandler。【参考方案2】:

我已经解决了我的问题,我不太喜欢这个解决方案,因为它很老套。它确实有效,所以我将它发布在这里以防万一。如果有人能帮助我理解更好的解决方案,我将不胜感激,如果/何时发生这种情况,我将编辑此答案或将其删除以支持另一个答案。

我以与我的问题大致相同的方式获得身份验证票证到期,然后如果我的 ExpireTimeSpan 设置剩余不到 50%,我认为它会更新(可能有点风险,但到目前为止这是最好的我'已经管理)。我使用它来计算在超时之前剩余的假定秒数,然后在决定何时向用户显示警告时使用此值。

在剃刀的顶部(部分):

@using Microsoft.AspNetCore.Authentication;

我的代码的重要部分(请随时提出改进建议):

secondsRemaining = "@(DateTime.Parse(((await AuthenticationHttpContextExtensions
                                             .AuthenticateAsync(Context))
                                             .Properties
                                             .Items)[".expires"])
                     .Subtract(DateTime.Now)
                     .TotalSeconds)";
// If secondsRemaining is less than half the expiry timespan then assume it will be re-issued
if (secondsRemaining < timeoutSeconds / 2) 
    secondsRemaining = timeoutSeconds;

【讨论】:

沿着这些线,但更简洁的可能是中间件的组合,该中间件在 cookie 中间件之后触发,该中间件在 HttpContext.Items 中设置一个变量,然后是一个 MVC 过滤器,该过滤器将其弹出到 ViewBag 中,然后可以访问在你看来。 我很好地寻找了一种方法来做到这一点(除了根据您的其他建议分叉 MS 代码)。除了后续页面加载之外,我发现无法在管道中的任何位置获得正确的票证到期值。反思一下,我的解决方案也没有实现这一点,所以我可以只使用上面的 AuthenticateAsync 行,随后假设刷新并将其粘贴在 HttpContext.Items 中以用于空闲超时部分。它仍然很老套,但至少它不会试图弄乱响应正文。 我意识到没有必要弄乱响应正文,甚至不需要使用 HttpContext.Items;我已经按照上面编辑的答案编辑了我的剃刀视图。这有点骇人听闻,因为它只是假设票已更新(尽管这可能是一个相当安全的假设)但它确实有效。再次感谢您的帮助@mackie

以上是关于如何在 Razor 页面中获取 ASP.NET 身份验证票证到期?的主要内容,如果未能解决你的问题,请参考以下文章

获取服务器异常以显示在错误页面 ASP.NET Razor 页面上

如何从 ASP.NET Core 的 Razor 视图中获取 Url 中 id 的值

覆盖 asp.net core razor 页面中的 razor 视图

如何在 asp.net core razor 页面中拒绝匿名用户?

如何在 ASP.Net Core Razor 页面上重定向

如何在 ASP.NET Core 2.0 Razor 页面中填充下拉列表