Merhaba arkadaşlar. Bu yazıyı Facebook’tan bir kardeşimin ricası üzerine yazma isteği duydum. Bir web projesi kodlarken sayfalara erişimi yetkilendirmeniz ve bazı sayfalara ulaşımları kısıtlamanız gerekir. Bizim okulda yaptığımız JSF ile e-ticaret projesinde ben bu tip erişim yetkilerini kodla yapmıştım. Ama bunun için daha kolay ve daha güvenli bir teknoloji mevcut. O da Apache Shiro. Apache Shiro’nun resmi sitesine ulaşmak için buraya tıklayabilirsiniz. JSF projesinde Apache Shiro kullanmak için yine bize sunulmuş bir kolaylık var. Maven ile Apache Shiro’ya ait gerekli olan her şey indirilir ve projeye eklenir. Şimdi NetBeans ortamında Maven nasıl kullanılır, Maven projesine JSF nasıl entegre edilir, Shiro ile yetkilendirme nasıl yapılır bunlara bakalım. NetBeans’ta yeni bir proje oluşturalım fakat bu kez projenin türü farklı olacak. Java Web yerine Maven seçeceğiz ve sağ kısımdan da Web Application bölümünü seçeceğiz. Bunları yapmadan önce NetBeans’ta Maven eklentisinin kurulu olması gerekmektedir. Tools>>Plugins yolunu takip edip Available Plugins bölümünden Maven eklentisini seçip kurabilirsiniz.
Web Application seçtikten sonra next butonuna tıklayalım. Proje adını soran ekran geliyor. JSF_Ve_Maven adını verelim projemize. Tekrar Next butonuna tıklayalım. Bir sonraki ekranda bize hangi sunucu sitemle çalışmak istediğimizi soruyor. Ben Apache Tomcat seçiyorum. Siz istediğinizi seçebilirsiniz. Sunucuyu seçtikten sonra Finish butonuna tıklıyoruz. Maven projemiz hazır. Maven projesi varsayılan olarak .html uzantılı bir sayfa ile gelir. Şimdi biz bunu silelim ve projeye sağ tıklayıp en alttan Properties kısmına tıklayalım. Yeni açılan pencerede de Frameworks kısmına tıklayalım.
Üstteki resimde gördüğünüz gibi Frameworks kısmı boş. Add butonuna tıklayalım ve Java Server Faces seçelim.Daha sonra da OK butonuna tıklayalım ve JSF projemize eklensin. JSF’i Maven projesine ekledikten sonra Web Pages kısmına dikkat ederseniz index.xhtml sayfası projemize eklenmiş oldu. Şimdi projemize yetkilendirmeleri tanımlayacağımız bir dosya ekleyeceğiz. Web Pages kısmına sağ tıklayıp New diyelim ve Empty File kısmını seçelim. Gelen ekranda dosya ismine shiro.ini adını verelim ve Finish butonuna tıklayalım. Shiro.ini dosyasının içine şu kodu yazalım:
[main] shiro.loginUrl = /giris.xhtml [users] yonetici = 963, admin misafir = 741, guest [roles] admin = * [urls] /index.xhtml = authc /giris.xhtml = authc /bilgilendirme.xhtml = authc /admin.xhtml=authc /logout = logout
Burada 4 bölüm görmektesiniz. [main] bölümünde kişilerin erişimi için yönlendirici bir sayfa tanımlıyoruz. Bu örnek için özel yetkili bir sayfaya erişmek istenildiğinde önce giriş sayfasına yönlendirilecek. [users] bölümünde kullanıcılar tanımlanıyor. Biz yonetici ve misafir adında 2 kullanıcı tanımladık ve “=” operatöründen sonra parolalarını atadık. “,” den sonra da yetkilerini bildirdik. “yonetici” adminlik yetkilerine sahip oldu, misafir de misafirlik yetkilerine. [roles] bölümünde ise admin=* admin yetkisine sahip kullanıcının tüm yetkilere sahip olduğunu söyledik. [urls] kısmında da sayfaların erişim yetkilerini tanımladık. 4 sayfa için de yetkilendirme tanımladık ve giriş yapılmadığı sürece bu sayfalara erişilemeyeceğini söyledik. Bir sayfanın herkese açık olması için authc yerine anon kullanmamız gerekir. Maven projesi açtığımızda bizim için gerekli .jar dosyalarını indirecek ve kullanıma hazırlayacak bir .xml dosyası bulunur. Bu dosyasının adı pom.xml’dir. Pom.xml içinde bulunan <dependencies> </dependencies> etiketleri arasına projede kullanmak istediğimiz .jar dosyasının kodunu yazarız ve projeyi build edince bu dosya indirilip projemize eklenir. Örneğin Apache Shiro için dependency kodu şu şekildedir:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.3</version> </dependency>
Bu örnek için kullandığım pom.xml dosyasının kodu şu şekildedir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kodcu</groupId> <artifactId>JSF_Ve_Maven</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>JSF_Ve_Maven</name> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jsf.version>2.1.16</jsf.version> <shiro.version>1.3.0-SNAPSHOT</shiro.version> </properties> <dependencies> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-api</artifactId> <version>${jsf.version}</version> </dependency> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-impl</artifactId> <version>${jsf.version}</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>6.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.1</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>6.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> <repositories> <repository> <id>deluan.repo</id> <name>Deluan's Public Repository</name> <url>http://deluan.googlecode.com/svn/releases</url> </repository> <repository> <id>deluan.snapshots</id> <name>Deluan's Public Snapshots Repository</name> <url>http://deluan.googlecode.com/svn/snapshots</url> <releases> <enabled>false</enabled> </releases> </repository> </repositories> </project>
Web.xml dosyası içinde de ayarlamalar yapmamız gerekmekte. Filtreler ekleyeceğiz yetkilendirme işlemleri için. Web.xml şu şekilde:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file>index.xhtml</welcome-file> </welcome-file-list> </web-app>
Diğer 4 sayfa ve managed bean kodumuz şu şekilde: index.xhtml Kodu
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Facelet Title</title> </h:head> <h:body> <h:form> <h:commandLink value="Admin Sayfası için tıklayın." action="/admin.xhtml"/> <br/> <h:commandLink value="Misafir Sayfası için tıklayın." action="/bilgilendirme.xhtml"/> </h:form> </h:body> </html>
admin.xhtml Kodu
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Facelet Title</title> </h:head> <h:body> <h:form> <h:outputText value="Admin.xhtml Sayfasına Giriş Yaptınız. Bu sayfaya sadece admin yetkisi olanlar giriş yapabilir."/> <h:commandLink value="Çıkış" action="#{shiroYetkilendirme.logout}"/> </h:form> </h:body> </html>
bilgilendirme.xhtml Kodu
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Facelet Title</title> </h:head> <h:body> <h:form> <h:outputText value="Bilgilendirme sayfasına geldiniz.Bu sayfaya misafir yetkisi olanlar ulaşabilir."/> <h:commandLink value="Çıkış" action="#{shiroYetkilendirme.logout}"/> </h:form> </h:body> </html>
giris.xhtml Kodu
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Facelet Title</title> </h:head> <h:body> <h:form> <h:messages showDetail="true" /> <h:outputText value="Kullanıcı Adı:"/> <h:inputText value="#{shiroYetkilendirme.kullaniciadi}"/> <h:outputText value="Parola:"/> <h:inputSecret value="#{shiroYetkilendirme.parola}"/> <h:commandButton value="Giriş Yap" action="#{shiroYetkilendirme.girisiKontrolEt}"/> </h:form> </h:body> </html>
ShiroYetkilendirme.java Kodu
import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import javax.faces.context.FacesContext; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.LockedAccountException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; @ManagedBean @RequestScoped public class ShiroYetkilendirme { private String kullaniciadi; private String parola; public String getKullaniciadi() { return kullaniciadi; } public void setKullaniciadi(String kullaniciadi) { this.kullaniciadi = kullaniciadi; } public String getParola() { return parola; } public void setParola(String parola) { this.parola = parola; } public String girisiKontrolEt(){ Subject currentUser = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(kullaniciadi, parola); try{ currentUser.login(token); } catch (UnknownAccountException uae ) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Giriş başarısız", "kullanıcı adınız yanlış")); return null; } catch (IncorrectCredentialsException ice ) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Giriş başarısız", "parolanız yanlış")); return null; } catch (LockedAccountException lae ) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Giriş başarısız", "Bu kullanıcı adı kilitli")); return null; } catch(AuthenticationException aex){ FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Giriş başarısız", aex.toString())); return null; } if(kullaniciadi.equals("yonetici")) return "admin.xhtml?faces-redirect=true"; else return "bilgilendirme.xhtml?faces-redirect=true"; } public String logout() { Subject currentUser = SecurityUtils.getSubject(); try { currentUser.logout(); } catch (Exception ex) { } return "giris.xhtml?faces-redirect=true"; } }
Java kodu içinde örneğin siz girisiKontrolEt metodunun return ettiği değeri sadece admin sayfasına yönelik yapar ve projeyi çalıştırıp misafir kullanıcı ile giriş yapmaya çalışırsanız server tarafından yetkiniz olmayan bir sayfaya erişmeye çalıştığınıza dair uyarı alırsınız.
Ekran çıktılarına da bakalım.

Adres çubuğuna http://localhost:8080/JSF_Ve_Maven/admin.xhtml yazmama rağmen giriş yapılmadığı için giriş sayfasına yönlendirildim.

shiro.ini dosyası içinde belirttiğimiz admin kullanıcının adını ve parolasını girdik. Butona tıkladık.

Yönetici adı ile giriş yaptığımız için admin sayfasına gönderildik. Admin sayfası sadece admin yetkisine sahip kullanıcılara açık.
Bu örnekte hem shiro tarafından hem kod tarafından bir güvenlik sağladık ve erişimi kısıtladık. Bu örneğin veri tabanı kullanarak yapılan kısmını da actionListener konusundan sonra yapacağım inşaAllah.Kodlarda anlamadığınız yer olduğunu sanmıyorum arkadaşlar 🙂 Esen kalın. Mutluluklar.
Çok açıklayıcı bir anlatımınız var. Teşekkür ediyorum. Yazılarınızın devamını bekliyorum.. 🙂
Rica ederim. Devam edeceğiz inşaAllah. 🙂
JSF derslerine yazılarınızla birlikte çalışıyorum. Gerçekten çok yardımcı oluyor çok basit ve anlaşılır bir anlatımınız var. Kendi adıma çok teşekkür ediyorum. Ellerinize sağlık. Bizi aydınlatmaya devam edin lütfen 🙂 Başarılarınızın devamını dilerim.. 🙂
Teşekkür ederim 🙂
Size de başarılar dilerim. 🙂
Çok Teşekkürler.