FormsAuthenticationTicket 超时后重新初始化 context.User
在我们的应用程序中,我们实现了基于角色的表单身份验证。这是使用 RoleModule 来处理的,我们将 Role 数据保存在 cookie 中, 每次我们从 cookie 中读取数据并实例化 IPrincipal
对象。此代码在 Application_OnPostAcquireRequestState
方法中执行:
HttpApplication application = source as HttpApplication;
HttpContext context = application.Context;
if (context.User.Identity.IsAuthenticated &&
(!Roles.CookieRequireSSL || context.Request.IsSecureConnection))
{
//Read the roles data from the Roles cookie..
context.User = new CustomPrincipal(context.User.Identity, cookieValue);
Thread.CurrentPrincipal = context.User;
}
这会初始化 context.User
对象。每次向服务器发出请求时,都会使用上述流程对用户进行身份验证。 在 Application_EndRequest
中,我们使用当前主体对象数据更新角色 cookie。
我们的 Global.asax 页面中有一个 FormsAuthentication_OnAuthenticate 方法,在这个方法中我们读取 cookie,更新 cookie,并更新 cookie 票如果过期了。另外,在此方法中,如果票证过期,我们尝试在会话对象中设置用户名值。
FormsAuthentication oldTicket = FormsAuthentication.Decrypt(context.Request.Cookies[FormsAuthentication.FormsCookieName].Value);
if(oldTicket != null)
{
if(oldTicket.Expired)
{
try
{
HttpContext.Current.Session["UserName"] = userName;
}
catch
{
//Log error.
}
FormsAuthentication newTicket = new FormsAuthenticationTicket(oldTicket.Version, oldTicket.Name, DateTime.Now,
DateTime.Now.AddMinutes(30), oldTicket.IsPersistent, oldTicket.UserData);
string encryptedTicket = FormsAuthentication.Encrypt(newTicket);
HttpCookie httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
if (isPersistent)
{
httpCookie.Expires = DateTime.Now.AddMinutes(300);
}
}
这是我们在 web.config 中的表单设置:
<forms defaultUrl="Default.aspx" domain="" loginUrl="Login.aspx" name=".ASPXFORMSAUTH" timeout="20" slidingExpiration="true" />
会话超时为 20 分钟。
问题:如果用户空闲时间超过 30 分钟(这是 FormsAuth 票证续订时间),context.User.Identity.IsAuthenticated
值 角色模块为 false
,context.User
设置为 NULL
。现在,当用户请求页面时,他会被重定向到登录页面。然而, 饼干还在那里。如果用户再次尝试请求页面,则 context.User.IsAuthenticated
属性将设置为 true
并且用户将转到 相应的页面。另外,在 FormsAuthentication_OnAuthenticate
方法中,当我尝试设置会话值时,它会抛出错误,因为会话对象为 NULL
。
我在这里想要实现的是,在持久cookie的情况下,在auth Ticket超时后,用户不应该被注销,即用户应该 被重新认证。
我怎样才能实现这个目标? 如果我没记错的话,设置 context.User
应该可以解决目的,但是我该怎么做呢?
其他信息:
票证过期后,我尝试请求页面,事件查看器显示一条错误消息:
Event code: 4005
Event message: Forms authentication failed for the request. Reason: The ticket supplied has expired.
Event time: 08-02-2012 20:02:05
Event time (UTC): 08-02-2012 14:32:05
Event ID: 048e3238ade94fd6a7289bac36d130ef
Event sequence: 76
Event occurrence: 2
Event detail code: 50202
Process information:
Process ID: 21692
Process name: w3wp.exe
Account name: IIS APPPOOL\ASP.NET v4.0 Classic
我正在使用存储在 web.config 中的标准机器密钥设置,而不是自动生成的设置。另外,我检查了进程 ID,所有错误都相同。
In our application we have implemented role-based forms authentication. This has been handled using a RoleModule, where we save the Role data in cookie,
and each time we read the data from the cookie and instantiate the IPrincipal
object. This code is executed in Application_OnPostAcquireRequestState
method:
HttpApplication application = source as HttpApplication;
HttpContext context = application.Context;
if (context.User.Identity.IsAuthenticated &&
(!Roles.CookieRequireSSL || context.Request.IsSecureConnection))
{
//Read the roles data from the Roles cookie..
context.User = new CustomPrincipal(context.User.Identity, cookieValue);
Thread.CurrentPrincipal = context.User;
}
This initializes the context.User
object. Each time a request is made to the server, the user is authenticated using the above flow.
In the Application_EndRequest
, we update the Roles cookie with the current principal object data.
There is FormsAuthentication_OnAuthenticate
method in our Global.asax
page, in which we read the cookie, update the cookie, and renew the
ticket if it is expired. Also, in this method, we try to set the username value in the session object if the ticket is expired.
FormsAuthentication oldTicket = FormsAuthentication.Decrypt(context.Request.Cookies[FormsAuthentication.FormsCookieName].Value);
if(oldTicket != null)
{
if(oldTicket.Expired)
{
try
{
HttpContext.Current.Session["UserName"] = userName;
}
catch
{
//Log error.
}
FormsAuthentication newTicket = new FormsAuthenticationTicket(oldTicket.Version, oldTicket.Name, DateTime.Now,
DateTime.Now.AddMinutes(30), oldTicket.IsPersistent, oldTicket.UserData);
string encryptedTicket = FormsAuthentication.Encrypt(newTicket);
HttpCookie httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
if (isPersistent)
{
httpCookie.Expires = DateTime.Now.AddMinutes(300);
}
}
Here is our forms setting in web.config:
<forms defaultUrl="Default.aspx" domain="" loginUrl="Login.aspx" name=".ASPXFORMSAUTH" timeout="20" slidingExpiration="true" />
The session timeout is 20 mins.
The issue: If a user is idle for more than 30 mins.(which is the FormsAuth ticket renewal time duration), the context.User.Identity.IsAuthenticated
value in
the role module is false
, and context.User
is set as NULL
. Now when a user requests a page he is redirected to the login page. However, the
cookie is still there. If again, the user tries to request a page, the context.User.IsAuthenticated
property is set to true
and the user is taken to the
respective page. Also, in FormsAuthentication_OnAuthenticate
method, when I try to set the session value it throws an error as the session object is NULL
.
What I want to achieve here is that in case of persistent cookie, after the auth ticket times out, the user should not be logged out, i.e. the user should
be re-authenticated.
How can I achieve this?
If I am not wrong, setting context.User
should solve the purpose, but how do I go about it?
Additional Info:
After the ticket expires and i try to request a page, the event viewer shows an error message:
Event code: 4005
Event message: Forms authentication failed for the request. Reason: The ticket supplied has expired.
Event time: 08-02-2012 20:02:05
Event time (UTC): 08-02-2012 14:32:05
Event ID: 048e3238ade94fd6a7289bac36d130ef
Event sequence: 76
Event occurrence: 2
Event detail code: 50202
Process information:
Process ID: 21692
Process name: w3wp.exe
Account name: IIS APPPOOL\ASP.NET v4.0 Classic
I am using standard machine key settings, stored in web.config and not an auto-generated one. Also, I checked the process ID and its the same for all the errors.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我终于解决了这个问题。发生的情况是,当 FormsAuthentication 票证超时时,
FormsAuthentication_OnAuthenticate
无法设置 context.User 对象,如 MSDN 文档 用于身份验证事件:原因是我没有使用用户的用户名设置
ticket.Name
。它是一个空字符串。因此,Authenticate 事件可能无法获取用户的身份并创建FormsIdentity
实例。作为解决方案,当我续订过期票据时,我还创建了一个 GenericIdentity 对象,然后使用它来设置 context.User。I finally resolved the issue. What was happening was that when the FormsAuthentication ticket timed out,
FormsAuthentication_OnAuthenticate
was not able to set the context.User object, as specified in the MSDN documentation for Authenticate event:The reason for that is that I am not setting
ticket.Name
with the username of the user. It is an empty string. Hence it might be the case that Authenticate event was not able to get the identity of the user and create theFormsIdentity
instance. As a solution, when I am renewing the expired ticket, I am also creating aGenericIdentity
object and then using it to setcontext.User
.