更新:它似乎正在尝试在ApplyResponseGrantAsync
中编写新的Cookie标头但不能,因为它的 会抛出异常标题已经发送。
更新:更清晰。如何在Web API请求期间将Set-Cookie
标头添加到XHR响应中?
TL; DR; 问题是应用程序是使用MVC进行身份验证的,但是大量使用Web API。 Web API请求即使使用Authentication
属性也不会滑动会话 - 几乎可以肯定,因为它是一个cookie响应。
我有一个组合的MVC和Web API应用程序。在大多数情况下,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);
有人知道如何强迫这个吗?
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.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
答案 0 :(得分:1)
根据CookieAuthenticationMiddleware
来源,如果您将SlidingExpiration
的{{1}}设置为true,那么您的会话将在每次请求时续订。您可以查看此code以了解自己。
我认为我会根据this line跟踪您的问题,以便更新Cookie,SignIn方法CookieAuthenticationOptions
的{{1}}属性应为true。我猜这是你的问题。
德尔>
<强>更新强>
这是另一种猜测,它可能会解决您的问题。基于this line,当只有50%的生命周期过去时,cookie会话将会更新。当我查看代码时,我认为没有办法覆盖它。我建议通过在每个请求上添加一个AllowRefresh
AuthenticationProperties
用户来更新cookie,从而在每个请求上创建一个新的cookie,如果在WebAPI或MVC中请求是否无关紧要
在Middleware
SingIn
基本上您可以将app.UseCookieAuthentication
设置为false,因为这与此变通方法无关,并且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();
});
SlidingExpiration
上的OnValidateIdentity
无法每30分钟重新加载一次用户声明,因为每个请求都会刷新IssuedUTC,如果需要,您必须修改此中间件并编写自定义CookieAuthentication
答案 1 :(得分:1)
通常,如果您在web.config中启用了slidingExpiration,它就会起作用。您无需手动重新生成新cookie。对于您的方案,我建议您使用跟踪工具,例如提琴手。刷新页面时,您可以从Fiddler查看是否重置了Cookie过期时间。
答案 2 :(得分:1)
由于AuthenticationHandler使用{{1连接回调,因此在将标头写入客户端时,ApplyResponseAsync()
以及随后的ApplyResponseGrantAsync()
和ApplyResponseChallengeAsync()
也会被调用一次在初始化阶段。这可以确保所有处理程序都有机会在标题发送给用户之前创建挑战或处理响应授权,这是我们可以的最后一个时间点修改回复。
所以为什么你需要在你的情况下建立一个cookie响应?!为什么你没有使用合适的超时间隔( Response.OnSendingHeaders()
)?或者至少使用持久性cookie 。
修改强>
通过在 web.config 中启用SlidingExpiration
解决了一些与您相比有问题的人解决后添加slidingExpiration
,如下所示。请让我知道结果:
cookieless="UseCookies"
答案 3 :(得分:1)
<强> TL; DR 强>
试试这个:只需将此代码添加到您的控制器,它会覆盖OnActionExecuted
,它会在每个操作后运行并手动添加标题:
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);
}
------------------以下是我的老答案--------------------- -------- 强>
首先考虑@Amirhossein Mehrvarzi的问题,如果出于任何原因需要创建自己的会话滑动,请考虑以下内容:
这只是一个建议,不确定它是否可行
您可以尝试覆盖:
OnActionExecuting(ActionExecutingContext filterContext)
(在行动前运行)
或Controller.OnActionExecuted(ActionExecutedContext filterContext)
(行动后运行)
控件的操作并在那里滑动会话。
它将在WebApi控制器和页面控制器上执行,您可以知道哪个控制器使用filterContext.ActionDescriptor.ActionName
中的this链接
这两种方法都会收到有用的输入,可以操作动作执行,如下面的链接所示:
答案 4 :(得分:0)
它最终成为我添加到管道中的过滤器,用于记录响应的内容。删除该过滤器后,即使是Web API请求,也会写入cookie。
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}");
}
}
}
感谢所有的帮助,我很抱歉这件小工件造成了这个问题!