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.