gpt4 book ai didi

c# - ASP.NET Identity 在每次请求时重新生成 Identity

转载 作者:行者123 更新时间:2023-11-30 16:08:23 30 4
gpt4 key购买 nike

我有一个 ASP.NET MVC 应用程序,我正在使用 ASP.NET Identity 2。我遇到了一个奇怪的问题。 ApplicationUser.GenerateUserIdentityAsync 为浏览器向我的网站发出的每个请求调用。我添加了一些 Trace.WriteLine,这是删除 IIS 输出后的结果:

IdentityConfig.Configuration called
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Content/bootstrap.css
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Scripts/modernizr-2.8.3.js
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Content/site.css
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Scripts/jquery-2.1.3.js
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Scripts/bootstrap.js
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Scripts/respond.js
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Scripts/script.js
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Glimpse.axd?n=glimpse_client&hash=8913cd7e
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Glimpse.axd?n=glimpse_metadata&hash=8913cd7e&callback=glimpse.data.initMetadata
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Glimpse.axd?n=glimpse_request&requestId=6171c2b0-b6e5-4495-b495-4fdaddbe6e8f&hash=8913cd7e&callback=glimpse.data.initData
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/Glimpse.axd?n=glimpse_sprite&hash=8913cd7e
ApplicationUser.GenerateUserIdentityAsync called url: http://localhost:54294/__browserLink/requestData/38254292a54f4595ad26158540adbb6a?version=2

如果我运行一个由模板创建的默认 MVC 应用程序,我会得到这个:

IdentityConfig.Configuration called

只有当我登录时,它才会调用 ApplicationUser.GenerateUserIdentityAsync

我找遍了所有我认为可能存在的地方,但没有找到任何结果。我正在使用(如果有帮助的话)

StructureMap 3
Elmah
Glimpse
ASP.NET MVC 5
EF6
ASP.NET Identity 2

附加信息

我在不使用 UserManage 的情况下直接将用户添加到数据库中。我不确定它是否会对身份造成任何问题。


更新

我已经删除了数据库,但它不再发生了。发生了什么事?

更新 2

它发生在我的谷歌浏览器中(我使用 glimpse 监控 SQL 连接),在删除存储的 cookie 后,它没有发生。其他浏览器登录会不会出现这个问题?

更新 3

同时注销 - 登录似乎可以暂时解决问题。

最佳答案

我遇到了同样的问题,在深入研究源代码和一些侦探工作后,我找到了解决方案。问题出在 SecurityStampValidator 内部,它用作默认的 OnValidateIdentity 处理程序。查看源码here .有趣的部分:

var issuedUtc = context.Properties.IssuedUtc;

// Only validate if enough time has elapsed
var validate = (issuedUtc == null);
if (issuedUtc != null)
{
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
validate = timeElapsed > validateInterval;
}

这部分针对每个请求运行,如果 validate 为真,则调用 getUserIdCallbackregenerateIdentityCallback(在您的跟踪输出中可见)。这里的问题是 issuedUtc 始终是创建 cookie 的日期,因此当 validateInterval 已过时,validate 始终为真。这解释了您遇到的奇怪行为。如果 validateInterval 是 10 分钟,验证逻辑将在创建 cookie 后 10 分钟或更长时间后针对每个请求运行(应用程序部署、cookie 清除、注销和重新登录时重置 cookie)。

SecurityStampValidator 应该根据先前的验证日期(或第一次检查时的签发日期)来决定是否验证,但它没有这样做。要使 issuedUtc 日期向前移动,有 3 种可能的解决方案:

  • 每个 validateInterval 重置 cookie,这意味着 SingOutSignIn。类似解决方案here .这似乎是一项代价高昂的操作,尤其是当 validateInterval 设置为仅几分钟时。
  • 利用 CookieAuthenticationOptions.SlidingExpiration 逻辑自动重新发布 cookie。在 this post 中解释得很好.

If SlidingExpiration is set to true then the cookie would be re-issued on any request half way through the ExpireTimeSpan. For example, if the user logged in and then made a second request 16 minutes later the cookie would be re-issued for another 30 minutes. If the user logged in and then made a second request 31 minutes later then the user would be prompted to log in.

在我的案例(内联网应用程序)中,用户在 30 分钟不活动后被注销是 Not Acceptable 。我需要默认的 ExpireTimeSpan,即 14 天。所以这里的选择是实现某种 ajax 轮询来延长 cookie 的生命周期。完成这个相当简单的场景听起来需要付出很多努力。

  • 我最终选择使用的最后一个选项是修改 SecurityStampValidator 实现以采用滑动验证方法。下面的示例代码。请记住将 Startup.Auth.cs 中的 SecurityStampValidator 替换为 SlidingSecurityStampValidator。我将 IdentityValidationDates 字典添加到原始实现中以存储每个用户的验证日期,然后在检查是否需要验证时使用它。

    public static class SlidingSecurityStampValidator
    {
    private static readonly IDictionary<string, DateTimeOffset> IdentityValidationDates = new Dictionary<string, DateTimeOffset>();

    public static Func<CookieValidateIdentityContext, Task> OnValidateIdentity<TManager, TUser, TKey>(
    TimeSpan validateInterval, Func<TManager, TUser, Task<ClaimsIdentity>> regenerateIdentityCallback,
    Func<ClaimsIdentity, TKey> getUserIdCallback)
    where TManager : UserManager<TUser, TKey>
    where TUser : class, IUser<TKey>
    where TKey : IEquatable<TKey>
    {
    if (getUserIdCallback == null)
    {
    throw new ArgumentNullException(nameof(getUserIdCallback));
    }

    return async context =>
    {
    var currentUtc = DateTimeOffset.UtcNow;
    if (context.Options != null && context.Options.SystemClock != null)
    {
    currentUtc = context.Options.SystemClock.UtcNow;
    }
    var issuedUtc = context.Properties.IssuedUtc;

    // Only validate if enough time has elapsed
    var validate = issuedUtc == null;
    if (issuedUtc != null)
    {
    DateTimeOffset lastValidateUtc;
    if (IdentityValidationDates.TryGetValue(context.Identity.Name, out lastValidateUtc))
    {
    issuedUtc = lastValidateUtc;
    }

    var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
    validate = timeElapsed > validateInterval;
    }

    if (validate)
    {
    IdentityValidationDates[context.Identity.Name] = currentUtc;

    var manager = context.OwinContext.GetUserManager<TManager>();
    var userId = getUserIdCallback(context.Identity);
    if (manager != null && userId != null)
    {
    var user = await manager.FindByIdAsync(userId);
    var reject = true;

    // Refresh the identity if the stamp matches, otherwise reject
    if (user != null && manager.SupportsUserSecurityStamp)
    {
    var securityStamp = context.Identity.FindFirstValue(Constants.DefaultSecurityStampClaimType);
    if (securityStamp == await manager.GetSecurityStampAsync(userId))
    {
    reject = false;
    // Regenerate fresh claims if possible and resign in
    if (regenerateIdentityCallback != null)
    {
    var identity = await regenerateIdentityCallback.Invoke(manager, user);
    if (identity != null)
    {
    // Fix for regression where this value is not updated
    // Setting it to null so that it is refreshed by the cookie middleware
    context.Properties.IssuedUtc = null;
    context.Properties.ExpiresUtc = null;
    context.OwinContext.Authentication.SignIn(context.Properties, identity);
    }
    }
    }
    }

    if (reject)
    {
    context.RejectIdentity();
    context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
    }
    }
    }
    };
    }
    }

关于c# - ASP.NET Identity 在每次请求时重新生成 Identity,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29378491/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com