Springde session yönetimini Spring Security ile yapmak isteyenler, eş zamanlı session yönetimini de basit bir şekilde spring-security.xml dosyasını değiştirerek yapabilirler. Bu noktada, gerekli düzenlemeleri yaptıktan sonra bile aynı bilgiler ile login olan kullanıcı, session oluşturamaması gerekirken bu işlemi yapabiliyorsa, çok büyük ihtimalle UserDetails sınıfınızdaki bir problem ile karşı karşıyasınız.
Buradaki sıkıntı, Spring Security'yi kullanabilmek için gerekli olan default kullanıcı arayüzü olan UserDetails'in implement edilmesi esnasında yaşanıyor. Bu arayüzü implement ederken oluşturduğumuz kendi kullanıcı sınıfımızda equals metodunu override etmemiz lazım. Çünkü bu metod içerisinde, sessionu oluşturan kullanıcıların concurrent olup olmadığını anlamamız için belirleyeceğimiz bir ya da birden fazla alanın karşılaştırılıp aynı olup olmadığının kontrol edilmesi gerekiyor. Bu metodu override ettikten sonra belirlediğimiz aynı alana sahip başka bir kullanıcı session oluşturmaya çalışınca Authentication hatası alacaktır.
Örnek vermek gerekirse;
Bir adet User sınıfımız olsun. Uygulamamızda bir kullanıcı session oluşturmuşken aynı kullanıcı adına sahip başka bir kullanıcının session oluşturmamasını istiyoruz. Örnek dosyalar aşağıdaki gibi olmalıdır:
web.xml
...
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
...
spring-security.xml
...
<session-management invalid-session-url="/no-sess.xhtml">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/no-sess.xhtml" />
</session-management>
...
MyUser.java
public class MyUser implements UserDetails
...
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyUser other = (MyUser) obj;
if (userName == null) {
if (other.userName != null)
return false;
} else if (!userName.equals(other.userName))
return false;
return true;
}
...
Bu konfigürasyonlar sonrasında bir kullanıcı session oluşturmuşken aynı kullanıcı adı ile başka bir session oluşturmaya çalışırsanız 'Maximum sessions of 1 for this principal exceeded' gibi bir hata mesajı alacaksınız.
( spring mvc, spring security, session management, concurrency control, max-sessions, UserDetails )
18 Şubat 2013 Pazartesi
7 Ocak 2013 Pazartesi
Zaman Çarkı (Elise) - Ken Grimwood
Evet, oldukça spoiler içerir şekilde:) 'Zaman Çarkı', orijial adıyla 'Elise' adlı kitaptan bahsedeceğim biraz.
Koridor Yayıncılık çıkışlı ve Ender Nail çevirili kitap Ken Grimwood tarafından yazılmış. Kitabın arka kapağındaki hikaye oldukça ilgiç gelmişti. Alma sebeplerimden birisi bu, diğeri de sanıyorum 'Olasılıksız' tarzı siyah beyaz, çekici kapağı oldu:) Kitapta 1600'lü yıllarda doğan ve sonrasında hiç yaşlanmayan bir kişnin hikayesi anlatılıyor. Kitap 2 ayrı senaryoda gidiyor. Bir tanesi 1600'lü yıllarda başlayıp devam eden, diğeri ise 1900'ün sonlarında devam eden iki hikaye olarak. Sonrasında tahmin edeceğiniz gibi bu hikayeler kesişiyor ve birleşiyor. Kem Grimwood'un bir kitabını ilk kez okudum. Açıkçası özellikle sürükleyici hikayelerde kahramanın ağzından olan anlatımı sevmediğim için bu kitabın anlatım kısmını oldukça iyi buldum. Yazar dışarıdan bir kişi olarak hikayeyi aktarıyor.
1600'lü yıllarda başlayan bölüm o yılların Fransa'sını kafanızda canlandırmanıza yetiyor ve sizi o tarihlere götürüyor. Romanlardaki en büyük sıkıntım olan farklı dillerdeki insan isimleri ve bu kişilerin saysının fazla olması bu noktada beni biraz zorladı açıkçası ama hikayeyi kaybetmeden devam edebildim. Ana karakterimiz bu yıllarda doğuyor ve ölümsüz yaşantısına başlıyor. Sevgilileri, eşleri oluyor. Onlar yaşlanırken ve ölürken kendisi genç olarak kalmaya devam ediyor. Bu hikaye, 1900'lü yılların sonralarında karakterin ismini Elise olarak değiştirmesi ve kendisini bu durumdan kurtarmasını umduğu bir profesör ile tanışması, durumunu anlatması ve çözüm araması kısmına geliyor. Çok da fazla detay vermek istemiyorum okumak isteyenler için. Genel hikaye bu şekilde.
Kitap, konunun ilginçliği ile birlikte sürükleyici başladı. Ana karakterin, Fransa'da o yıllarda yaşadıkları, kölecilik zamanları ve o zamanlardaki ayaklanmalar oldukça iyi aktarılmış. Özellikle karakterin, yaşlanmadığını keşfedip sevdiklerini birer birer kaybetmeye başlayınca hissettikleri ve büründüğü ruh hali, sizi de kolaylıkla etkiliyor. Fakat sonlara yaklaştığımda hikaye hızlanmaya başladı. Eskiden bölümler arası 1-2 yıl ilerlerken bir anda 10 yıl 20 yıl ilerlemeye başladık. Elise'in bir ülkedeki yaşantısı anlatılırken sonraki bölümde bir anda hiç alakası olmayan başka bir ülke ve yaşama geçti. Sanki yazar kitabın sonralına doğru sıkılmış da bir an önce bitirmek istemiş gibi. Tarih sayfalarında duyduğumuz isimlerle geçen o kadar güzel işlenmiş hikayeler, çok basit bir sonla bitti açıkçası. Kitabın ortalarına kadar oldukça heyacanlı okurken son bölümlerinde ise sıkıldım ve hayal kırıklığına uğradım. Fakat 3-4 gün içinde normal bir tempoda bitirilebilecek, fazla beklenti olmadan zaman geçirmek için fena olmayan bir kitap. Eğer 'The Man From Earth' filmini izlediyseniz ve hoşunuza gittiyse, bu kitabı da muhtemelen seveceksiniz.
( zaman çarkı, elise, ken grimwood, Ender Nail, Koridor Yayıncılık, kitap, roman, inceleme )
21 Aralık 2012 Cuma
HttpURLConnection İle Web Sayfası İçeriği Almak
Java'da basit bir şekilde ve herhangi bir ekstra kütüphane kullanmadan HTTPURLConnection sınıfı ile bir web sitesine bağlanıp içeriğini almak konusunda ufak bir kod parçası ile örnek vereceğim. Bu işlemi, bir web sayfasını tamamen String olarak çekip parse etmek ya da GET, POST vs. ile çalışan bir REST servisi çağırmak için kullanabilirsiniz.
Aşağıdaki örnekte http://www.mehmetaktas.org?param1=VALUE1¶m2=VALUE2 şeklinde bir adresin içeriğini çekeceğiz.
public String getURLContent(){
String uri = "http://www.mehmetaktas.org?param1=value1¶m2=value2";
HttpURLConnection connection = null;
try {
//Bağlantımızı açıyoruz
URL url = new URL(uri);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");//POST, PUT, DELETE ...
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
/*
* Content-Type, Content-Language gibi request property set etmek
* istersek aşağıdaki metod kullanılabilir:
* connection.setRequestProperty("Content-Language", "tr-TR");
*/
//Response'ı alıyoruz
InputStream is = connection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder response = new StringBuilder();
while ((line = rd.readLine()) != null) {
response.append(line);
response.append('\r');
}
rd.close();
is.close();
return response.toString();
}
catch (Exception e) {}
finally {
if (connection != null) {
connection.disconnect();
}
}
}
( httpurlconnection, screenscraper, java, url, connection, web page, parse, rest, get, post )
Aşağıdaki örnekte http://www.mehmetaktas.org?param1=VALUE1¶m2=VALUE2 şeklinde bir adresin içeriğini çekeceğiz.
public String getURLContent(){
String uri = "http://www.mehmetaktas.org?param1=value1¶m2=value2";
HttpURLConnection connection = null;
try {
//Bağlantımızı açıyoruz
URL url = new URL(uri);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");//POST, PUT, DELETE ...
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
/*
* Content-Type, Content-Language gibi request property set etmek
* istersek aşağıdaki metod kullanılabilir:
* connection.setRequestProperty("Content-Language", "tr-TR");
*/
//Response'ı alıyoruz
InputStream is = connection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder response = new StringBuilder();
while ((line = rd.readLine()) != null) {
response.append(line);
response.append('\r');
}
rd.close();
is.close();
return response.toString();
}
catch (Exception e) {}
finally {
if (connection != null) {
connection.disconnect();
}
}
}
( httpurlconnection, screenscraper, java, url, connection, web page, parse, rest, get, post )
13 Aralık 2012 Perşembe
Operation Not Allowed After ResultSet Closed
Merhaba,
JDBC kullanarak Java'da veritabanı bağlantısı yapıp veri çekmeye çalışırken "rs.next()"ile bir veriye ulaşırız. Bu metodu çağırdığımız esnada "java.sql.SQLException: Operation not allowed after ResultSet closed" şeklinde bir taha alıyorsanız olası bir kaç sebebi var. Bu sebeplerin başında ResultSet nesnenizi kullanırken bir döngü ya da başka bir metodun içerisinde "rs.close()" metodunu kullanarak ya da nesneyi null yaparak hata alma durumunuz vardır. Bu problemi, biraz araştırarak çözebilirsiniz fakat "Ben ResultSet nesnemi kesinlikle kapatmıyorum ya da null yapmıyorum neden böyle oluyor?" derseniz sebebi bulmak biraz zor olabilir. Böyle bir durum başıma geldi ve çözümünden bahsedeceğim.
Java tarafında veritabanına bağlanırken ResultSet nesnesini doldurmak için bir Statement ya da PreparedStatement nesnesi kullanırız. Bu nesne teoride bir adet ResultSet'e hizmet verebilir. Statement kapandığı zaman, ona bağlı olan ResultSet de kapanır. Sıkıntı da aslında buradan kaynaklanıyor. Özellikle uzun sürede sonuç getiren bir sorgu çalıştırdığınız zaman Statement, uygulama sunucusunun pool ayarlarında tanımlı olan Statement Timeout'una takılıyor. Bu esnada Statement, uygulama sunucusu tarafından kapatıldığı için ResultSet nesnesi de kapanmış oluyor. Siz de herhangi bir veri çekmeye çalıştığınızda en tepede bahsettiğim hatayı alıyorsunuz.
Bu sorunu, SQL sorgunuzu optimize ederek ya da uygulama sunucusundaki kullandığınız veritabanı pool'unun Statement Timeout süresini uzatarak çözebilirsiniz. Glassfish için bu ayara "Resources > JDBC > JDBC Connection Pools > POOL_ADI > Advanced" menüsünden ulaşabilirsiniz.
( java, jdbc, statement, resultset, pool, timeout, sqlexception, connection )
JDBC kullanarak Java'da veritabanı bağlantısı yapıp veri çekmeye çalışırken "rs.next()"ile bir veriye ulaşırız. Bu metodu çağırdığımız esnada "java.sql.SQLException: Operation not allowed after ResultSet closed" şeklinde bir taha alıyorsanız olası bir kaç sebebi var. Bu sebeplerin başında ResultSet nesnenizi kullanırken bir döngü ya da başka bir metodun içerisinde "rs.close()" metodunu kullanarak ya da nesneyi null yaparak hata alma durumunuz vardır. Bu problemi, biraz araştırarak çözebilirsiniz fakat "Ben ResultSet nesnemi kesinlikle kapatmıyorum ya da null yapmıyorum neden böyle oluyor?" derseniz sebebi bulmak biraz zor olabilir. Böyle bir durum başıma geldi ve çözümünden bahsedeceğim.
Java tarafında veritabanına bağlanırken ResultSet nesnesini doldurmak için bir Statement ya da PreparedStatement nesnesi kullanırız. Bu nesne teoride bir adet ResultSet'e hizmet verebilir. Statement kapandığı zaman, ona bağlı olan ResultSet de kapanır. Sıkıntı da aslında buradan kaynaklanıyor. Özellikle uzun sürede sonuç getiren bir sorgu çalıştırdığınız zaman Statement, uygulama sunucusunun pool ayarlarında tanımlı olan Statement Timeout'una takılıyor. Bu esnada Statement, uygulama sunucusu tarafından kapatıldığı için ResultSet nesnesi de kapanmış oluyor. Siz de herhangi bir veri çekmeye çalıştığınızda en tepede bahsettiğim hatayı alıyorsunuz.
Bu sorunu, SQL sorgunuzu optimize ederek ya da uygulama sunucusundaki kullandığınız veritabanı pool'unun Statement Timeout süresini uzatarak çözebilirsiniz. Glassfish için bu ayara "Resources > JDBC > JDBC Connection Pools > POOL_ADI > Advanced" menüsünden ulaşabilirsiniz.
( java, jdbc, statement, resultset, pool, timeout, sqlexception, connection )
12 Aralık 2012 Çarşamba
Aspx Html Çift Title Sorunu
Aspx kullanarak oluşturulan web sitelerinde işimiz yarayan MasterPage'den kaynaklı olarak bir duplicate <title> tag problemi yaşıyor olabilirsiniz. Bunun başlıca sebebi, master sayfasının <head> tagleri arasında bulunan ContentPlaceHolder aslında. Her bir sayfada farklı bir title kullanmak istediğimizde <title>Sayfa Bilgisi</title> kodunu master sayfadaki <head> tagi arasına koymak yerine master sayfasındaki <head> tagi içerisine bir adet ContentPlaceHolder atarız. Sonrasında oluşturduğumuz sayfada bu ContentPlaceHolder'ın içeriğini koyacağımız yere <title> tagimizi koyarız. Bu işlem, sayfa render edildiğinde biri boş olmak üzere iki adet <title> tagine sebep olmaktadır. Bunun sebebi, server tarafında çalışan ve <head> içerisinde bulunan ContentPlaceHolder'ın, <title> tagini bulamayıp kendisinin ekstra bir adet daha üretmesidir.
Sorunun çözümü ise basit. Title bilgisini <title> tagi arasına koymak yerine sayfanın en tepesindeki <%@ Page Title=""... içerisine koymak. Sırasıyla sorunlu kod ve düzeltilmiş kod aşağıdaki gibi olmalıdır.
Hatalı Kod:
<%@ Page Title="" Language="C#" MasterPageFile="~/Mstr.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
<title>BASLIK</title>
...
</asp:Content>
Doğru Kod:
<%@ Page Title="BASLIK" Language="C#" MasterPageFile="~/Mstr.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
...
</asp:Content>
( aspx, .net, <title>, <head>, çift title, contentplaceholder, masterpage, content, runat="server" )
Sorunun çözümü ise basit. Title bilgisini <title> tagi arasına koymak yerine sayfanın en tepesindeki <%@ Page Title=""... içerisine koymak. Sırasıyla sorunlu kod ve düzeltilmiş kod aşağıdaki gibi olmalıdır.
Hatalı Kod:
<%@ Page Title="" Language="C#" MasterPageFile="~/Mstr.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
<title>BASLIK</title>
...
</asp:Content>
Doğru Kod:
<%@ Page Title="BASLIK" Language="C#" MasterPageFile="~/Mstr.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
...
</asp:Content>
( aspx, .net, <title>, <head>, çift title, contentplaceholder, masterpage, content, runat="server" )
Kaydol:
Kayıtlar (Atom)