C# Web API请求上的滑动会话
更新:它似乎正在尝试在C# Web API请求上的滑动会话,c#,.net,asp.net-mvc,session,asp.net-web-api2,C#,.net,Asp.net Mvc,Session,Asp.net Web Api2,更新:它似乎正在尝试在ApplyResponseGrantAsync中写入新的cookie标头,但无法写入,因为它引发了一个异常,即标头已经发送。 更新:更清楚。如何在Web API请求期间将Set Cookie头添加到XHR响应中 TL;博士问题是应用程序通过MVC进行身份验证,但大量使用Web API。Web API请求不会滑动会话,即使它们使用了身份验证属性——几乎可以肯定,因为这是一个cookie响应 我有一个结合了MVC和WebAPI的应用程序。在大多数情况下,MVC视图只加载发出大量
ApplyResponseGrantAsync
中写入新的cookie标头,但无法写入,因为它引发了一个异常,即标头已经发送。
更新:更清楚。如何在Web API请求期间将Set Cookie
头添加到XHR响应中
TL;博士问题是应用程序通过MVC进行身份验证,但大量使用Web API。Web API请求不会滑动会话,即使它们使用了
身份验证
属性——几乎可以肯定,因为这是一个cookie响应
我有一个结合了MVC和WebAPI的应用程序。在大多数情况下,MVC视图只加载发出大量Web API请求的SPA这很好,但会话滑动不适用于Web API请求。
我可以看到CookieAuthenticationHandler
在ApplyResponseGrantAsync
中滑动会话的位置,但我需要在每个Web API请求上强制执行该操作
model.Properties.IssuedUtc = new DateTimeOffset?(this._renewIssuedUtc);
model.Properties.ExpiresUtc = new DateTimeOffset?(this._renewExpiresUtc);
if (this.Options.SessionStore != null && this._sessionKey != null)
{
await this.Options.SessionStore.RenewAsync(this._sessionKey, model);
ClaimsIdentity identity = new ClaimsIdentity((IEnumerable<Claim>) new Claim[1]
{
new Claim("Microsoft.Owin.Security.Cookies-SessionId", this._sessionKey)
}, this.Options.AuthenticationType);
model = new AuthenticationTicket(identity, (AuthenticationProperties) null);
}
string cookieValue = this.Options.TicketDataFormat.Protect(model);
if (model.Properties.IsPersistent)
cookieOptions.Expires = new DateTime?(this._renewExpiresUtc.ToUniversalTime().DateTime);
this.Options.CookieManager.AppendResponseCookie(this.Context, this.Options.CookieName, cookieValue, cookieOptions);
model.Properties.IssuedUtc=新的日期时间偏移量?(此.\u续订IssuedUtc);
model.Properties.ExpiresUtc=新的DateTimeOffset?(此.\u续订ExpiresUtc);
if(this.Options.SessionStore!=null&&this.\u sessionKey!=null)
{
等待this.Options.SessionStore.RenewaAsync(this.\u sessionKey,model);
索赔实体标识=新索赔实体((IEnumerable)新索赔[1]
{
新声明(“Microsoft.Owin.Security.Cookies SessionId”,this.\u sessionKey)
},this.Options.AuthenticationType);
模型=新的AuthenticationTicket(identity,(AuthenticationProperties)null);
}
string cookieValue=this.Options.TicketDataFormat.Protect(模型);
if(model.Properties.IsPersistent)
cookieOptions.Expires=新的日期时间?(此._renewExpiresUtc.ToUniversalTime().DateTime);
this.Options.CookieManager.AppendResponseCookie(this.Context,this.Options.CookieName,cookieValue,cookieOptions);
有人知道怎么强迫吗
身份验证设置
app.UseCookieAuthentication(新的CookieAuthenticationOptions
{
AuthenticationType=DefaultAuthenticationTypes.ApplicationOkie,
LoginPath=新路径字符串(“/Account/Login”),
Provider=新CookieAuthenticationProvider
{
OnValidateIdentity=SecurityStampValidator.OnValidateIdentity(
validateInterval:TimeSpan.FromMinutes(15),
regenerateIdentity:(管理器,用户)=>user.GenerateUserIdentityAsync(管理器))
},
slidengexpiration=true,
ExpireTimeSpan=时间跨度。从分钟(1)
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie,TimeSpan.FromMinutes(5));
app.useTowFactoryMemberBrowserCookie(DefaultAuthenticationTypes.TwoFactoryRememberBrowserCookie);
根据CookieAuthenticationMiddleware
来源,如果您将CookieAuthenticationOptions
的SlidingExpiration
设置为true,您的会话将在每次请求时续订。你可以检查一下,看看你自己
我想我找到了您的问题,基于为了更新cookie,SignIn方法的AuthenticationProperties
的AllowRefresh
属性应该为true。我猜这是你的问题。
如果我没有记错的话,您正在使用'ApplicationSignInManager.SignInAsync()'登录您的用户。不幸的是,基于[implementation][2],无法传递参数来设置'AllowRefresh',因此我建议重写SignInAsync方法并将'AllowRefresh'设置为true,然后看看它是否有效。
更新
这是另一种猜测,它可能会解决你的问题。基于cookie的会话将在其生命周期仅过了50%时续订,当我查看代码时,我发现没有办法覆盖它。我建议通过添加一个中间件来更新cookie,该中间件将在每个请求上插入用户,从而在每个请求上创建一个新的cookie,无论请求是在WebAPI还是MVC中
在app.UseCookieAuthentication之后添加中间件
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(15),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(1)
});
app.Use(async (context, next) =>
{
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (result != null)
{
result.Properties.ExpiresUtc = null;
result.Properties.IssuedUtc = null;
context.Authentication.SignIn(result.Properties, result.Identity);
}
await next();
});
app.UseCookieAuthentication(新的CookieAuthenticationOptions
{
AuthenticationType=DefaultAuthenticationTypes.ApplicationOkie,
LoginPath=新路径字符串(“/Account/Login”),
Provider=新CookieAuthenticationProvider
{
OnValidateIdentity=SecurityStampValidator.OnValidateIdentity(
validateInterval:TimeSpan.FromMinutes(15),
regenerateIdentity:(管理器,用户)=>user.GenerateUserIdentityAsync(管理器))
},
slidengexpiration=true,
ExpireTimeSpan=时间跨度。从分钟(1)
});
应用程序使用(异步(上下文,下一步)=>
{
var result=wait context.Authentication.authenticateSync(DefaultAuthenticationTypes.ApplicationOkie);
如果(结果!=null)
{
result.Properties.ExpiresUtc=null;
result.Properties.IssuedUtc=null;
context.Authentication.SignIn(result.Properties,result.Identity);
}
等待下一个();
});
基本上,您可以将SlidingExpiration
设置为false,因为这与此解决方法无关,而且通过这种方式CookieAuthentication
上的OnValidateIdentity
无法每30分钟重新加载一次用户声明,因为每次请求都会刷新IssuedUTC,如果需要,您必须修改此中间件并编写一个自定义的OnValidateIdentity
通常,如果您在web.config中启用slidingExpiration,它将起作用。您无需手动重新生成新cookie。对于您的场景,我建议您使用跟踪工具,例如Fiddler。刷新页面时,可以从Fiddler检查cookie过期时间是否重置
在读取标头时,ApplyResponseAsync()
,以及随后的ApplyResponseGrantAsync()
和ApplyResponseChallengeAsync()
,也将调用一次
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(15),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(1)
});
app.Use(async (context, next) =>
{
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (result != null)
{
result.Properties.ExpiresUtc = null;
result.Properties.IssuedUtc = null;
context.Authentication.SignIn(result.Properties, result.Identity);
}
await next();
});
<forms loginUrl="/account/login" name="authy" path="/" slidingExpiration="true" cookieless="UseCookies"/>
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.ActionDescriptor.ActionName=="YourWebAPIName")
{
filterContext.HttpContext.Response.AddHeader("Set-Cookie","CookieName=CookieValue");
}
//You can replace the former if with a more general one like checking:
//filterContext.Result.GetType() and see if it is of the type JsonResult
base.OnActionExecuted(filterContext);
}
public class WebApiResponseFilter : ActionFilterAttribute
{
private ILogUtils logUtils;
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
if (logUtils == null)
{
logUtils = StructureMapConfig.Container.GetInstance<ILogUtils>();
}
var httpContext = HttpContext.Current;
var actionDescriptor = actionExecutedContext.ActionContext.ActionDescriptor;
var requestId = httpContext.Request.Headers.GetValues("X-RequestId").First();
var userId = httpContext.User.Identity.GetUserId();
var userName = httpContext.User.Identity.GetUserName();
var responseContent = actionExecutedContext.Response.Content;
if (responseContent == null)
{
logUtils.LogUsage($"RESPONSE LOG ipAddress:{httpContext.Request.UserHostAddress} requestId:{requestId} userId:{userId} userName:{userName} action:{actionDescriptor.ControllerDescriptor.ControllerName}.{actionDescriptor.ActionName} response:n/a");
}
else
{
var response = Task.Run(async () => await responseContent.ReadAsStringAsync()).Result;
response = AesManager.EncryptData(response);
logUtils.LogUsage($"RESPONSE LOG ipAddress:{httpContext.Request.UserHostAddress} requestId:{requestId} userId:{userId} userName:{userName} action:{actionDescriptor.ControllerDescriptor.ControllerName}.{actionDescriptor.ActionName} response:{response}");
}
}
}