最近同仁分享一個 Form Authentication Ticket 過期的問題(為什麼我的 APS.Net Form Authentication 在 timeout 時間還沒到前就失效了)。
如果 timeout 時間設定為 20 分鐘,而 Ticket 是 1:00:00 產生的,到期時間是 1:20:00。
如果設定 SlidingExpiration , 到期的時間會在每次回 Server 就更新嗎?
Timeout 時間跟你想的不一樣嗎?
我們可以看 MSDN 上的說明「FormsAuthentication.SlidingExpiration」,
如果提出要求,而且一半以上的逾時間隔已經經過滑動期限,重設為有效的驗證 cookie 的到期時間。
以下面圖來看(timeout設為4分鐘),就是使用者要在 紅色時間區段中有對 Server Request 過,才會更改「過期時間」。
如果使用者剛好只在綠色時間區段中 Request Server,
然後在 06:28:14 後去 Request Server , 就會 Timeout 了哦!
那有什麼解法呢? 同事的解法就是設定成 Double 的時間,
例如原本設定 20 分鐘 timeout,就設定成 40 分鐘 Timeout。
另一個方式,就是手動去更改 Ticket 的過期時間。
實作
1.建立一個 Web Forms 專案
1.1.建立 Login.aspx (登入後,切到 Default.aspx)
Login.aspx
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox runat="server" ID="txtUserId"></asp:TextBox>
<asp:Button runat="server" ID="btnLogin" Text="Login" OnClick="btnLogin_OnClick"/>
</div>
</form>
</body>
</html>
Login.aspx.cs
protected void btnLogin_OnClick(object sender, EventArgs e)
{
FormsAuthentication.RedirectFromLoginPage(txtUserId.Text,false);
}
1.2.建立 Default.aspx (列出 Ticket 相關資訊,並更新 Ticket 的過期時間)
Default.aspx
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ListBox ID="lsIdentityInfos" runat="server"></asp:ListBox>
<hr/>
<asp:Button runat="server" ID="btnAddIdentity" Text="Add Identity Info" OnClick="btnAddIdentity_OnClick"/>
</div>
</form>
</body>
</html>
Default.aspx.cs
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
FormsAuthenticationTicketInfo();
}
protected void btnAddIdentity_OnClick(object sender, EventArgs e)
{
FormsAuthenticationTicketInfo();
}
void FormsAuthenticationTicketInfo()
{
var identityInfos = new StringBuilder();
FormsAuthenticationTicket ticket = ((FormsIdentity)Context.User.Identity).Ticket;
var timeoutValue = (TimeSpan)(ticket.Expiration - ticket.IssueDate);
identityInfos.AppendLine($"Timeout:{timeoutValue}");
identityInfos.AppendLine($",IssueDate:{ticket.IssueDate.ToString("hh:mm:ss", null)}, Expiration:{ticket.Expiration.ToString("hh:mm:ss", null)}");
var now = DateTime.Now;
identityInfos.AppendLine($",Now:{now.ToString("hh:mm:ss", null)}");
//var span = now - ticket.IssueDate;
//var span2 = ticket.Expiration - now;
//identityInfos.AppendLine($"now - ticket.IssueDate:{span}, Expiration - now:{span2}");
lsIdentityInfos.Items.Add(identityInfos.ToString());
//var newTicket = FormsAuthentication.RenewTicketIfOld(ticket);
//Response.Write($"{newTicket.Expiration.ToString("hh:mm:ss", null)}");
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (Request.IsAuthenticated && Context.User.Identity is FormsIdentity)
{
//原本的 Ticket
var orgTicket = ((FormsIdentity)Context.User.Identity).Ticket;
//取出 Timeout 的值
var timeoutValue = (TimeSpan)(orgTicket.Expiration - orgTicket.IssueDate);
var now = DateTime.Now;
//重新再計算新的過期時間
var newExpiration = now + timeoutValue;
//建立一個新的 Ticket
var newTicket = new FormsAuthenticationTicket(orgTicket.Version, orgTicket.Name, now, newExpiration, orgTicket.IsPersistent, orgTicket.UserData);
if (newTicket.Expiration > orgTicket.Expiration)
{
// 建立 FormsAuthentication Cookie
HttpCookie objCookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(newTicket));
// 蓋掉舊的 Cookie
Response.Cookies.Add(objCookie);
}
}
}
}
註:我是在 Page 的 OnPreRender 去更新過期時間,您也可以將它放在 底層 Class 中,或是在 Global.asax 的 Application_AuthenticateRequest 中哦!
2.Web.config 設定使用 Forms Authentication
在 web.config system.web 區段中加入
<authentication mode="Forms">
<forms loginUrl="login.aspx"
name=".rmau"
slidingExpiration="true"
timeout="4" />
</authentication>
<authorization>
<deny users="?"/>
</authorization>
當每次 Request Server 後,都會重新設定 Ticket 的過期時間哦! 如下,
測試專案: AuthenticationTicket-SlidingExpiration
參考資料
為什麼我的 APS.Net Form Authentication 在 timeout 時間還沒到前就失效了
FormsAuthentication.SlidingExpiration
Manually sliding Forms AuthenticationTicket expiration
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^