Bir önceki blog yazımda ASP.NET uygulamalarında sessionless bir sayfanın nasıl üretilebileceği konusuna değinmiştim. O yazının içerisinde de esrarengiz bir not ile ilerleyen günlerde sessionless sayfa kullanımına güzel bir örnek vereceğimiz belirtmiştim. İşte; şu an o yazıyı okuyorsunuz.
Günümüzde web uygulamalarını daha kullanışlı hale getirmek için bir çok araç kullanmakta ve kullanıcı memnuniyetini arttıracak özellikleri uygulamamıza katmanın yollarını aramaktayız. Son yıllarda benim dikkatimi çeken özelliklerden birisi de bir web sitesinde oturum açtıktan sonra, eğer oturum zamanı bitmeye yaklaşırsa açılan bir popup pencerede “İşlem yapmazsanız az sonra oturumunuz kapatılacaktır. Oturumunuzu devam ettirmek istiyor musunuz?” şeklindeki mesajlardır. Bu tip pencerelerle özellikle bankacılık uygulamalarında karşılaşabiliyoruz. Ben kendi literatürümde bu pencerelere “oturum uzatma pencereleri” adını verdim. Bu yazıda bir ASP.NET uygulamasında oturumun biteceğini istemci tarafında nasıl yakalayacağımızı ve sunucu tarafında yapacağımız işlemlerle bu süreci nasıl uzatacağımıza, yani oturum uzatma pencerelerini nasıl kullanacağımıza bakacağız.
İlk olarak sorunun kaynağını görelim ki, az sonra göreceğimiz dolambaçlı yolu neden kullanmak zorunda olduğumuzu iyice anlayabilelim. Session timeout değerinin 20 dakika olduğunu baz olarak şöyle bir senaryo üzerinde konuşalım:
– Kullanıcı oturum açar ve işlemler yapar.
– Kullanıcı 19 dakika boyunca sitede hiçbir işlem yapmaz.
– Oturumun bitmesine 1 dakika kala kullanıcıya uyarı penceresi açar ve deriz ki: “Oturumu devam ettirmek istiyorsan aşağıdaki butona tıkla”.
– Kullanıcı butona tıklarsa oturumu 20 dakika daha uzatılır, bir işlem yapmazsa oturumu sonlandırılır.
Adımlar oldukça anlaşılır ve son derece basit gibi görünüyor. Ancak burada iki kritik nokta var; birincisi 19 dakika boyunca işlem yapmayan bir kullanıcıdan nasıl haberdar olacağız ve onu nasıl uyaracağız. Zira HTTP sadece istek-cevap mekanizması üzerinde çalışır ve kullanıcıdan istek gelmeden biz sunucudan A isimli kullanıcıya hiçbir çağrı yapamayız. Tahmin edeceğiniz üzere bu sorunu istemci tarafında çözeceğiz, yani JavaScript kullanacağız. İkinci sorunumuz ise bir önceki yazımı okuyan arkadaşların tahmin edebileceği sorundur. 19 dakika geçtikten sonra istemci tarafında uygulamamıza ait bir sayfa açarsak zaten session otomatik olarak uzatılacaktır. Yani kullanıcı butona dahi tıklamadan, kullanıcı onay vermeden biz oturumunu uzatırız. İşte bu sorunu aşmak için de istemci tarafında popup pencerede açılan sayfanın kullanıcıya ait oturumu etkilememesi gerekmektedir.
“Ayrı bir popup penceresi açmadan, aynı pencere içerisinde bir modalpopup açarak uyarı verirsek tekrar sunucuya gitmeyeceğimiz için böyle bir sorunla uğraşmayız” durumu aklınıza gelebilir. Evet doğrudur, modalpopup kullanarak böyle karışık bir yola girmeden de kullanıcıya uyarı verilebilir, ancak ben bu yöntemin çok kullanışlı olduğunu düşünmüyorum. Zira aynı pencere içerisinde açılacak bir modalpopup kullanıcının aktif penceresinde doğrudan görünmeyeceği için kullanıcının bu pencereden haberi dahi olmayabilir. O nedenle benim burada anlattığım örneğin çok daha kullanışlı olacağından eminim, zira kullanıcının ekranına fırlayan bir popup penceremiz olacak!
Oluşturacağımız projeyi local makinamızda düzgün şekilde test edebilmemiz için uygulamayı IIS üzerinde host etmemiz gerekmektedir.
Zira oluşturacağımız uyarı sayfasının varolan oturum nesnesine erişmesini engellemek için ayrı bir alt uygulamaya ihtiyaç duymaktayız. Tavsiyem; öncelikli olarak dizin tanımlamalarını yapıp IIS’de projeyi tanımlanamız, sonrasında da Visual Studio’da projeyi Open Web Site menüsünden açmanız şeklinde olacak.
NOT: Eğer varolan bir proje üzerinde çalışıyorsanız, ve bu projeye local dizinden erişiyorsanız projeye IIS üzerinden devam etmek için;
– Web Site şablonuyla açılan bir projeyi kapatıp, IIS’de dizinleri belirterek tanımlamanız, daha sonra da Open Web Site menüsünde açılan pencerede sağ kısımdan Local IIS’i seçmeniz ve uygulama dizinini seçmelisiniz.
– Web Application şablonunu kullanıyorsanız, Solution Explorer’da proje üzerine sağ tıklayıp Properties > Web sekmesinden Servers bölümündeki Use Local IIS Web server seçeneğini seçmeniz gerekmektedir(Belirtilen dizin IIS’de yer almıyorsa Create Virtual Directory butonuna tıklamayı unutmayın)
Nihayetinde bu işlemleri tamamladığınızda IIS ve Solution Explorer’da aşağıdakine benzer bir dosya yapınız olmalıdır.
IIS’de oluşturulan projeyi Visual Studio’da açtığınızda subsite dizininin içeriği getirilmeyecektir. Bu sorunu gidermek için resimde anlattığım gibi
işlemleri gerçekleştirirseniz en alttaki kutu içerisinde görüleceği üzere Solution Explorer’da ayrı bir proje gibi yer alacaktır.
Sayfaları hızlı şekilde test edebilmek için öncelikli olarak session timeout değerini azaltıyoruz. Ana dizindeki web.config dosyasında sessionState elementine ait timeout değerini “1” yapıyorum(normal uygulamalarda bu değer “20” olmakta).
web.config <system.web> <compilation debug="true" targetFramework="4.0"/> <sessionState timeout="1"/> </system.web>
Default.aspx isimli bir sayfada oturum açmamızı sağlayan ve o an oturumun açık olup olmadığını kontrol eden iki adet butonumuz var. Bu sayfa arka planda oturumun bitmesine ne kadar süre kaldığını hesaplayacak ve az bir zaman kaldığında da popup pencerede kullanıcıya uyarı sayfasını gösterecektir. Bu işlemleri istemci tarafında yapabilmek için bir JavaScript fonksiyonu oluşturmamız ve fonksiyon içerisinde timer kullanarak oturumun bitmesine ne kadar süre kaldığını hesaplamamız gerekiyor. JavaScript’te setTimeout metodu timer işlevini görmektedir. setTimeout(functionName,interval) yapısındaki fonksiyonun 2. parametresi tetiklenmenin kaç saniyede bir yapılacağını, 1. parametre ise her tetiklenmede hangi fonksiyonun çağrılacağını belirler. clearTimeout metodu ise açık olan bir timer’ın kapatılmasını sağlar. Sayfamızın kodları aşağıdaki gibi:
Default.aspx <body id="bodyElement" runat="server"> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Oturum Aç" /> <asp:Button ID="Button2" runat="server" onclick="Button2_Click" Text="Oturumu Kontrol Et" Width="159px" /> <br /><br /> <asp:Label ID="lblMessage" runat="server"></asp:Label> </div> </form> <script type="text/javascript"> var popupTime = (<%=Session.Timeout %> * 60000) - 20000; //20 saniye kala uyarı penceresi açacağız function countTime() { var timer = setTimeout("countTime()", 1000); if (popupTime > 0) { popupTime -= 1000; } else { clearTimeout(timer); window.open('subsite/OturumKapamaUyari.aspx', 'window1', 'width=350,height=250,scrollbars=yes'); } } </script> </body>
Default.aspx.cs public partial class _Default : System.Web.UI.Page { protected void Button1_Click(object sender, EventArgs e) { string username = "dummyuser"; Session["username"] = username; lblMessage.Text = "Oturum açıldı. Merhaba " + username + "<br/>Timeout süresi: " + Session.Timeout; } protected void Button2_Click(object sender, EventArgs e) { if (Session["username"] == null) lblMessage.Text = "Oturum kapanmış"; else lblMessage.Text = "Oturum açık. " + Session["username"]; } protected void Page_PreRender(object sender, EventArgs e) { if (Session["username"] != null) // Authentication kullanilirsa if(User.Identity.IsAuthenticated) bodyElement.Attributes.Add("onload", "countTime();"); } }
Sayfanın ilk yüklenişinde sunucudan elde ettiğimiz Session.Timeout değeri ile istemci tarafında ne zaman popup pencereyi çıkacağımızı belirliyoruz. countTime isimli JavaScript fonksiyonu sayfanın PreRender olayında eğer oturum açılmışsa <body> elementinin onload olayına eklenmektedir.
Dolayısıyla oturum açıldıktan sonra sayfa yüklendiğinde session süresi işlemeye başlıyor, bizim de JavaScript timer’ımız işlemeye başlayarak zamanı geldiğinde pencereyi açıyor. Butonlarımızdan ilkinin click olayında basit bir oturum açıyor, ikinci butonun click olayında ise sadece o an oturum açılmış mı diye kontrol ediyoruz.
countTime fonksiyonunun içerisinde görüleceği gibi subsite dizininin altında OturumKapamaUyari.aspx isimli bir dosyamıza link vermişiz. Bu dosya uygulamanın alt dizininde yer alıyor, ancak daha önceden de anlattığımız gibi bu dizin IIS’de ayrı bir dizin olarak tanımlandığı için dosya farklı şekilde davranmakta ve ana uygulamanın Session nesnesine erişmemektedir.
subsite dizini altındaki web.config ve OturumKapamaUyari.aspx dosyalarının kodları aşağıdaki gibidir.
subsite/web.config <system.web> <compilation debug="true" targetFramework="4.0"/> <sessionState cookieName="subsite"></sessionState> </system.web>
subsite/OturumKapamaUyari.aspx <body style="background-color: #336699; color: White;"> <form id="form1" runat="server"> <div style="font-family: Segoe UI Semibold; font-size: 18px;"> Açık olan oturumunuz kapatılacaktır. <br /> Kalan süre: <strong><span id="remainingTime">20</span> saniye</strong> <br /> <br /> <asp:Button ID="btnContinue" runat="server" Text="Oturumun Devam Etmesi İçin Tıklayın" Width="266px" OnClick="btnContinue_Click" /> <br /> <span id="endSession" style="visibility: hidden">Oturum zaman aşımına uğradı ve sonlandırıldı</span> </div> </form> </body> <script type="text/javascript"> showTime(); function showTime() { var value = document.getElementById('remainingTime').innerHTML; var timeoutParam = setTimeout("showTime()", 1000); if (value > 0) { document.getElementById('remainingTime').innerHTML = value - 1; } else { document.getElementById('btnContinue').style.visibility = 'hidden'; document.getElementById('endSession').style.visibility = 'visible'; clearTimeout(timeoutParam); } } function closeWindow() { window.close(); return false; } function countTime() { var timer = setTimeout('showTime()', 1000); } </script>
subsite/OturumKapamaUyari.aspx.cs public partial class OturumKapamaUyari : System.Web.UI.Page { protected void btnContinue_Click(object sender, EventArgs e) { Response.Redirect("../OturumDevam.aspx"); } }
OturumKapamaUyari.aspx sayfasında görsellik açısından süreyi geriye doğru sayan bir kontrol eklemiş olsam sayfanın kodlarını biraz karmaşık gibi gösteriyor, ancak bu şekliyle sayfa daha kullanışlı olmuştur diye düşünüyorum. Aynı zamanda sayfa geriye kaç saniye sayacağını da bu kontrolün içerisindeki metin değerinden anlamaktadır(). Burada asıl önemli olan kullanıcının bu pencereden oturumun kapanacağını öğrenmesi ve bunu engellemesi için btnContinue isimli butona tıklayarak ana uygulamadaki bir sayfayı çağırmasıdır. Böylece oturum nesnesi ötelenecek, bir başka deyişle uzatılacaktır. Uygulamanın ana dizinine OturumDevam.aspx dosyası ekliyoruz. Bu sayfa teknik olarak sunucu tarafında oturumu uzatmak dışında bir işlem yapmayacağı için talep gördükten hemen sonra sayfayı JavaScript ile kapatmamız mantıklı olacaktır. Bunun için de sayfanın HTML kodlarının aşağıdaki gibi olması yeterlidir.
OturumDevam.aspx <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script language="javascript"> onLoad(); function onLoad() { window.close(); } </script> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html>
Uygulamamız hazır. Timeout değerimiz 1 dakika olduğu için Default.aspx sayfasını açıp Oturum Aç butonuna tıkladıktan sonra yaklaşık 40 saniye sonra popup pencerede uyarı sayfamızın çıkacağını göreceğiz. Eğer bu penceredeki süre bitmeden butona tıklayıp, yaklaşık 30 saniye bekleyip Default.aspx sayfasındaki Oturumu Kontrol Et butonuna tıklarsak halâ oturumun devam ettiğini görürüz. Eğer popup penceredeki süre bittiğinde hiçbir işlem yapmamışsak, Default.aspx sayfasında dönüp Oturumu Kontrol Et butonuna tıklarsak oturumun kapandığını görebiliriz.
Bu şekilde sitemizde oturum açmış olan bir kullanıcıya oturum zamanı bitmeden bir süre önce uyarı vermiş ve isteğe bağlı olarak oturum süresini uzatmış olduk. Günümüzde özellikle bankacılık uygulamalarına giriş yapabilmek için birkaç adımdan oluşan bilgi doğrulama ekranlarını ve cep telefonumuza gelen SMS’lerdeki şifreleri beklemek zorundayız. Bu tip uygulamalarda birkaç dakikalığına işlem yapmadığımızda farkında olmadan oturumumuzun kapatılması ve yeniden aynı süreçten geçip sisteme giriş yapmamız her ne kadar güvenlik için gerekli olsa da kullanıcı memnuniyetini azaltan bir durum. Örneğimizin güvenlik açısından hiçbir sakıncası olmadığı gibi gereksiz zaman kayıplarını azaltmak için web uygulamalarında kullanabileceğimiz güzel bir pratik olacaktır.
session magdurlari icin alternatif bir calisma daha var http://programmerramblings.blogspot.com/2010/08/aspnet-40-session-timeout-control.html
Bu tarz bir ihtiyacı modelpopup giderecekse, modal popup uygulamak oldukça kolay. Bu hazır eklentiyi paylaştığın için teşekkürler Emre.
çok teşekkürler. tüm ayrıntıları ile konuyu ele aldığınız için.
An itibariyle ufak değişikliklerle sistemimde uyguladım. Bilgilendirme için teşekkürler.
Bir not da ben ekleyeyim:
* subsite altındaki sayfa html olarak da eklenebiliyor. redirect işlemini de bu durumda istemci tarafında yapmak gerekiyor. html siteyi iis’te daha sorunsuz yayınlayabiliriz
Serdara katılıyorum popupda html sayfa açılır, devam et düğmesine tıklanınca ; pupup ın “opener” sayfası ana sayfaya yönlendirilir, popup kapatılır. session yenilenir. Herkes mutlu olur
Ben birşey sormak istiyorum.
Bu session TimeOut hiçbir zaman işe yaramıyor.
Benim sistemimdeki sessionlar kafasına göre bazen 2 dakikada bazen 5 dakikada kapanıyor. IIS den değiştirdim timeout u arttırdım çözüm olmadı.
Sebebi ne olabilri acaba?
Application Pool Idle Timeout süresi de önemli, sunucudaki ram ayarı vs.
Bunlardan sıyrılmak için Session durumunu veritabanında tutabilirsiniz:
https://msdn.microsoft.com/en-us/library/ms178586.aspx
Geç cevap verdiğim için üzgünüm
Session’ın kafasına göre sonlanması sunucu veya IIS ile ilgili bir problem olduğuna işarettir. Normal şartlarda session 20 dakika dolmadan timeout olmaz, ancak sunucu tarafında uygulamanın veya IIS’in restart edilmesini gerektiren birşeyler oluyorsa session zaten otomatik olarak silinir. Uygulamanın kendine ayrılan belleği doldurması, uygulamanın içerisindeki önemli dosya ve klasörlerde(web.config, App_Data vb) değişiklikler yapılması… gibi. Bunları araştırmanı tavsiye ederim.
bodyElement.Attributes.Add(“onload”, “countTime…
Yukardaki satırda bodyElement kısmını program tanımıyor. Bunun için ayrıca bir using referansı kullanmalımıyım. Başka bir nedenimi var acaba?