2015-03-31 54 views
3

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

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

这事发生在我的谷歌浏览器(我使用监视窥SQL连接),并删除存储的cookie之后,它并没有发生。可以在其他浏览器中登录导致此问题?

更新3

而且注销 - 登录似乎解决的问题是暂时的。

+0

这个想必不会帮助,但只是一个想法。你尝试在发布模式下运行而不是调试吗?调试模式可能会带来很多额外的电话 – 2015-04-01 02:20:56

+0

@ScottSelby有趣!我在发布模式下运行它,一切都很好。然后我将它发布到我的服务器,并再次发生:| – 2015-04-01 14:10:33

+0

ASP.NET身份使用会话吗?这听起来像是在请求之间没有保持session-id时的情况。如果是这样,请尝试设置一个虚拟会话变量。 – sisve 2015-04-07 08:00:02

回答

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的日期,所以已过时,validate始终为真。这解释了你正在经历的奇怪行为。如果validateInterval为10分钟,则验证逻辑将针对在创建cookie(部署应用程序,清除cookie,在注销时重新登录时cookie被重置)后10分钟或更长时间内发出的每个请求运行。

SecurityStampValidator应基于上一个验证日期(或第一次检查时的发布日期)作出是否验证的决策,但它并不这样做。为了使issuedUtc日期向前推进有3级可能的解决方案:

  • 重置的cookie每个validateInterval,这意味着SingOutSignIn。类似的解决方案here。这似乎是一个昂贵的操作,特别是如果validateInterval设置为几分钟。
  • 利用CookieAuthenticationOptions.SlidingExpiration逻辑自动重新发布cookie。在this post中解释得非常好。

如果SlidingExpiration设置为true,那么该cookie将被通过的ExpireTimeSpan任何要求的一半重发。例如,如果用户登录,然后在16分钟后发出第二次请求,则cookie将被重新发布另外30分钟。如果用户登录,然后由第二请求31分钟后则用户将被提示登录。

在我的情况闲置30分钟后被登出(内联网应用程序)的用户是不能接受的。我需要有默认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); 
            } 
           } 
          } 
         }; 
        } 
    } 
    
+0

感谢您的回答。虽然我不再在那个项目上工作,但你的答案似乎是对的,所以我让它接受了答案 – 2016-08-16 18:09:27

0

有导致此复位可能存在两个问题:

  1. 该Cookie IssuedUtc日期是无效的(issuedUtc财产上的Cookie是null
  2. 身份验证的时间间隔设置不正确。

在您的startup.cs类中,您会找到Cookie认证配置。而且里面的配置是,应设置类似下面的函数委托:

public partial class Startup 
{ 
    public void ConfigureAuth(IAppBuilder app) 
    { 
     ... 

     app.UseCookieAuthentication(new CookieAuthenticationOptions 
     { 
      AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 
      LoginPath = new PathString("/Account/Login"), 
      Provider = new CookieAuthenticationProvider 
      { 
       OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
        validateInterval: TimeSpan.FromMinutes(30), 
        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) 
      } 
     });    

     ... 
    } 
} 

检查,看看你的validateInterval没有被设置为较低的值。在上面的情况下,在发布有效cookie 30分钟后会有一个数据库(user.GenerateUserIdentityAsync)。在你的情况下,它可能会设置为每秒钟的低值。

如果您正在使用注销无处不在功能(安全标记),对validateInterval的更改将允许cookie保持有效,直到调用OnValidateIdentity函数。