.net 如何知道 OWIN cookie 何时到期?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23090706/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:43:07  来源:igfitidea点击:

How to know when OWIN cookie will expire?

.netasp.net-mvc-5session-cookiesowin

提问by FrankO

I would like to create some kind of countdown timer based on the time the OWIN cookie will expire. I am using OWIN with MVC 5 and from what I understand SlidingExpiration is on by default. I do not use 'session' as I need this app to live within a web farm (I dont plan on deploying a session database).

我想根据 OWIN cookie 的过期时间创建某种倒数计时器。我将 OWIN 与 MVC 5 一起使用,据我所知,默认情况下 SlidingExpiration 处于启用状态。我不使用“会话”,因为我需要在网络农场中使用此应用程序(我不打算部署会话数据库)。

回答by Phyo

All you need is to get hold of the CookieValidateIdentityContextduring the cookie validation stage. Once you get it, extract whatever you need and keep them as Claimor some other way that you prefer.

您所需要的只是CookieValidateIdentityContext在 cookie 验证阶段掌握。一旦你得到它,提取你需要的任何东西,并Claim以你喜欢的方式或其他方式保留它们。

For MVC 5 with Asp.NET Identity 2.0, you need to perform two steps:

对于带有 Asp.NET Identity 2.0 的 MVC 5,您需要执行两个步骤:

  1. Define custom OnValidateIdentity, extract cookie information, and keep it as Claim.

    public class Startup
    {
      public void Configuration(IAppBuilder app)
      {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
          AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
          Provider = new CookieAuthenticationProvider
          {
            OnValidateIdentity = MyCustomValidateIdentity //refer to the implementation below
          }
        }
      }
    
    
      // this method will be called on every request
      // it is also one of the few places where you can access unencrypted cookie content as CookieValidateIdentityContext
      // once you get cookie information you need, keep it as one of the Claims
      // please ignore the MyUserManager and MyUser classes, they are only for sample, you should have yours
      private static Task MyCustomValidateIdentity(CookieValidateIdentityContext context)
      {
        // validate security stamp for 'sign out everywhere'
        // here I want to verify the security stamp in every 100 seconds.
        // but I choose not to regenerate the identity cookie, so I passed in NULL 
        var stampValidator = SecurityStampValidator.OnValidateIdentity<MyUserManager<Myuser>. MyUser>(TimeSpan.FromSeconds(100), null); 
        stampValidator.Invoke(context);
    
        // here we get the cookie expiry time
        var expireUtc = context.Properties.ExpiresUtc;
    
        // add the expiry time back to cookie as one of the claims, called 'myExpireUtc'
        // to ensure that the claim has latest value, we must keep only one claim
        // otherwise we will be having multiple claims with same type but different values
        var claimType = "myExpireUtc";
        var identity = context.Identity;
        if(identity.HasClaim(c=> c.Type == claimType))
        {
          var existingClaim = identity.FindFirst(claimType);
          identity.RemoveClaim(existingClaim); 
        }
        var newClaim = new Claim(claimType, expireUtc.Value.UtcTicks.ToString());
        context.Identity.AddClaim(newClaim);
    
        return Task.FromResult(0);
      }
    }
    
  2. Access your Claimin your controller methods

    // since expiry time has now become part of your claims, you now can get it back easily
    // this example just returns the remaining time in total seconds, as a string value
    // assuming this method is part of your controller methods
    
    public string RemainingTime()
    {
      var identity = User.Identity as ClaimsIdentity;
      var claimType = "myExpireUtc";  //NOTE: must be the same key value "myExpireUtc" defined in code shown above
    
      if(identity != null && identity.HasClaim(c=> c.Type == claimType))
      { 
        var expireOn = identity.FindFirstValue(claimType); 
    
        DateTimeOffset currentUtc = DateTimeOffset.UtcNow;
        DateTimeOffset? expireUtc = new DateTimeOffset(long.Parse(expireOn), TimeSpan.Zero);
    
        var remaining = (expireUtc.Value - currentUtc).TotalSeconds;
    
        return remaining.ToString();
      }
      return string.Empty;
    }
    
  1. 定义 custom OnValidateIdentity,提取 cookie 信息,并将其保存为Claim.

    public class Startup
    {
      public void Configuration(IAppBuilder app)
      {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
          AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
          Provider = new CookieAuthenticationProvider
          {
            OnValidateIdentity = MyCustomValidateIdentity //refer to the implementation below
          }
        }
      }
    
    
      // this method will be called on every request
      // it is also one of the few places where you can access unencrypted cookie content as CookieValidateIdentityContext
      // once you get cookie information you need, keep it as one of the Claims
      // please ignore the MyUserManager and MyUser classes, they are only for sample, you should have yours
      private static Task MyCustomValidateIdentity(CookieValidateIdentityContext context)
      {
        // validate security stamp for 'sign out everywhere'
        // here I want to verify the security stamp in every 100 seconds.
        // but I choose not to regenerate the identity cookie, so I passed in NULL 
        var stampValidator = SecurityStampValidator.OnValidateIdentity<MyUserManager<Myuser>. MyUser>(TimeSpan.FromSeconds(100), null); 
        stampValidator.Invoke(context);
    
        // here we get the cookie expiry time
        var expireUtc = context.Properties.ExpiresUtc;
    
        // add the expiry time back to cookie as one of the claims, called 'myExpireUtc'
        // to ensure that the claim has latest value, we must keep only one claim
        // otherwise we will be having multiple claims with same type but different values
        var claimType = "myExpireUtc";
        var identity = context.Identity;
        if(identity.HasClaim(c=> c.Type == claimType))
        {
          var existingClaim = identity.FindFirst(claimType);
          identity.RemoveClaim(existingClaim); 
        }
        var newClaim = new Claim(claimType, expireUtc.Value.UtcTicks.ToString());
        context.Identity.AddClaim(newClaim);
    
        return Task.FromResult(0);
      }
    }
    
  2. 访问您Claim的控制器方法

    // since expiry time has now become part of your claims, you now can get it back easily
    // this example just returns the remaining time in total seconds, as a string value
    // assuming this method is part of your controller methods
    
    public string RemainingTime()
    {
      var identity = User.Identity as ClaimsIdentity;
      var claimType = "myExpireUtc";  //NOTE: must be the same key value "myExpireUtc" defined in code shown above
    
      if(identity != null && identity.HasClaim(c=> c.Type == claimType))
      { 
        var expireOn = identity.FindFirstValue(claimType); 
    
        DateTimeOffset currentUtc = DateTimeOffset.UtcNow;
        DateTimeOffset? expireUtc = new DateTimeOffset(long.Parse(expireOn), TimeSpan.Zero);
    
        var remaining = (expireUtc.Value - currentUtc).TotalSeconds;
    
        return remaining.ToString();
      }
      return string.Empty;
    }
    

I use this approach to remind my application users to extend their session before session time out.

我使用这种方法来提醒我的应用程序用户在会话超时之前延长他们的会话。

Credit to this post How do I access Microsoft.Owin.Security.xyz OnAuthenticated context AddClaims values?

归功于这篇文章如何访问 Microsoft.Owin.Security.xyz OnAuthenticated 上下文 AddClaims 值?

回答by Ricardo Saracino

I just want to build on the first answer, i had problems getting it to work as i was using a custom user store. though the implementation seems simpler for myself

我只想建立在第一个答案的基础上,我在使用自定义用户存储时遇到了问题。虽然实现对我来说似乎更简单

public static class ApplicationCookieValidateIdentityContext
    {
        public static Task ApplicationValidateIdentity(CookieValidateIdentityContext context)
        {          
            var identity = context.Identity;

            if (identity.HasClaim(c => c.Type ==  "expires"))
            {
                var existingClaim = identity.FindFirst( "expires");
                identity.RemoveClaim(existingClaim);
            }

            if (context.Properties.ExpiresUtc == null) return Task.FromResult(0);

            context.Identity.AddClaim(new Claim("expires", context.Properties.ExpiresUtc.ToString()));

            return Task.FromResult(0);
        }
    }

I also created a filter that adds the expiration to a cookie

我还创建了一个过滤器,将过期时间添加到 cookie

// make sure its defaulted because the claim is not set on the login callback
var expires = DateTimeOffset.Now.AddMinutes(
    Convert.ToDouble(ConfigurationManager.AppSettings["SessionTimeInMinutes"]));


if (identity.HasClaim(c => c.Type == "expires"))
{
    expires = DateTimeOffset.Parse(identity.FindFirstValue("expires"));
}

cookieHeaderValues.Add(new SessionCookeHeaderValue("expiresAt", expires.ToString("O"), expires));