Apache Wicket + Spring Security ve Hatırla beni (remember-me) seçeneği

Prş 10 Haz 2010 18:19:49 | 1 yorum


Ne yaptım: Basit bir web uygulaması içerisinde, güvenlik mekanizması yerleştirdim.


Nasıl yaptım :  Apache Wicket + Spring and Spring Security ve remember me(hatırla beni) seçeneği gerçekleştirildi.

Neden bu yazıyı yazdım : İnternet üzerinde Spring ve Spring Security ile alakalı bir çok yazı okudum fakat her ne hikmetse tam olarak istediğim bütün bir çözüm bulamadım (Belki ben yanlış aratmış olabilirim). Bu eksikliği kapatmak için bu yazıyı yazdım.

Detaylar

İşlemlere başlamadan önce, kullanıcı adı, şifre ve rollerin alınacağı tablo ve verileri aşağıda veriyorum.

CREATE TABLE `musteri_rol` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `musteri_id` int(11) DEFAULT NULL,
  `rol_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Data for the table `musteri_rol` */

insert  into `musteri_rol`(`id`,`musteri_id`,`rol_id`) values (1,1,1),(2,1,2),(3,2,1);

----------------------------------------------------------------------

CREATE TABLE `musteriler` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `eposta` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `musteriler` */

insert  into `musteriler`(`id`,`eposta`,`password`) values (1,'altuga@gmail.com','6e8cd7f743f933158ce8e777136cda9c'),(2,'mehmetc@gmail.com','6e8cd7f743f933158ce8e777136cda9c');

----------------------------------------------------------------------

DROP TABLE IF EXISTS `roller`;

CREATE TABLE `roller` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `rol_ismi` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `roller` */

insert  into `roller`(`id`,`rol_ismi`) values (1,'ROLE_USER'),(2,'ROLE_ADMIN');



Kullanıdığım ana teknolojiler.

  • Spring version : 3.0.2.RELEASE 
  • Spring Security version :3.0.2.RELEASE 
  • Wicket version : 1.4.8


Daha da detay  : 

İşte hikaye; Bir adet gizli ve sadece bazı kişilerin erişebileceği bir sayfamız var; İşte adımlar : 

  • Kullanıcı sayfaya gelir 


  • Kullanıcı "Go to Secured Page " bağlantısına tıklar

  • Sistem kullanıcıdan kullanıcı adı ve şifre ister



    • Eğer doğru kullanıcı ise; kişi gizli sayfayı görür, başarı !!! ve kişinin tarayıcısına çerez atılır. Bu çerezin amacı kişinin daha sonra sisteme geldiğinde tekrardan kullanıcı adı ve şifre girmesinin önüne geçmek bu sayede kullanıcı memnuniyetini artar. 



    • Eğer kullanıcı yanlış bilgi girerse : 



    • Eğer kişinin gizli sayfayı görmeye hakkı yoksa o zaman : 


Şimdi gerekli parçaları açıklamaya çalışalım

MusteriDAO.java : Bu sınıfın amacı kullanıcıyı onaylamaktır; yani kullanıcı bilgileri doğru mu ? Değil mi ? Bu sorunun cevabını bu sınıf cevaplar



    @Repository
    @Transactional
    @Service
    public class MusteriDAO {

        @Autowired
        private SessionFactory sessionFactory;

        /**
         * finds user according to email address
         * @param eposta, email address
         * @return Musteri object
         */
        public Musteri bul(String eposta) {
            Session session = this.sessionFactory.getCurrentSession();
            Criteria criteria = session.createCriteria(Musteri.class);
            criteria.add(Restrictions.eq("eposta", eposta));
            List sonuc =  criteria.list();
            if (sonuc!=null && sonuc.size()>0) {
                 Musteri musteri = sonuc.get(0);
                return musteri;
            }else {
                return null;
            }
        }

        /**
         * Checks users email and password
         * @param eposta   email address
         * @param sifre    password(encryted)
         * @return  the result, true meaans everything is ok, if false then no way to go
         */
        public boolean sifreDogruMu(String eposta, String sifre) {
            Session session = this.sessionFactory.getCurrentSession();
            Criteria criteria = session.createCriteria(Musteri.class);
            criteria.add(Restrictions.eq("eposta", eposta));
            criteria.add(Restrictions.eq("sifre", sifre));
            List sonuc =  criteria.list();
            if (sonuc!=null && sonuc.size()>0) {
               return true;
            }else {
                return false;
            }
        }
}

spring-context.xml : Basit bir  Spring ayar dosyası. Öncelikle hibernate  ve mysql ayarları için gerekli tanımlamaları yapıyoruz.


       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:beans="http://www.springframework.org/schema/lang"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.0.2.xsd
       http://www.springframework.org/schema/lang
       http://www.springframework.org/schema/lang/spring-lang-2.0.xsd"
        >
    
    
    
    
    

    
        

        
            
                com.foo.data.Musteri
                com.foo.data.Rol

            
        

        
            
                org.hibernate.dialect.MySQLDialect
                true
            
        
    


    

    
        
        
        
        
        
    

    
        
        
    
   


spring-security.xml : Güvenlik ile alakalı tüm işlemlerin olduğu dosya burası. Bu çok büyük bir dosya, o yüzden bu dosyayı parçalara ayırarak anlatmak en hayırlısı olacak. 

  
A - 
 
                
        
 

Kullanıcı giriş sayfasına herhangi bir güvenlik çemberine almaması için filters="none" diyoruz. Aksi takdirde giriş sayfasını da şifreleyip sonsuz ve saçma güvenlikli bir sistem oluşturmak an meselesi.


B - 

    
        
            
        
  

Spring sisteminin güzelliği ve zerafetini iyi yansıtan bir kısımdayız. Bu noktada web.xml dışında içsel filitreler kullanıyoruz. Filitre demek, her isteği (respond) alıp bakıp değerlendiren kod parçaları demek. Örneğin rememberMeProcessingFilter amacı kullanıcı sisteme başarılı bir şekilde bağlandığında, ona bir çerez atmak; sonra efendim ; logoutFilter amacı ise gelen giden istekler arasında "logout" diye kelime aramak, yani kullanıcı logout bağlantısına tıklamış ise, kullanıcıyı sistemden atar varsa çerezi siler.


C - 

    

    
                class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>


    
                class="com.foo.datasource.MyAuthenticationProvider">
        
        
    

Çok mu karışlık ? Hemen kısımlara bölelim.

MyUserDetailsService.java :  Bu sınıfın büyük bir amacı vardır;  Kullanıcıları eposta adreslerine göre bulur (bu sistemde kullanıcı adı == eposta adresi ), tabii bunu yaparken arka planda MusteriDAO.java kullanılmaktadır.


public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private MusteriDAO musteriDao;

    public UserDetails loadUserByUsername(String eposta) throws UsernameNotFoundException, DataAccessException {
        Musteri musteri = musteriDao.bul(eposta);
        if (musteri!=null) {
            
            ArrayList ga = new ArrayList();
            List roller = musteri.getRolListesi();
            for(Rol rol : roller) {
                 ga.add(new GrantedAuthorityImpl(rol.getRolIsmi()));
            }
            GrantedAuthority[] grantedAuthorities = new GrantedAuthority[ga.size()];
            ga.toArray(grantedAuthorities);

            MyUserDetails myUserDetails = new MyUserDetails(eposta, musteri.getSifre(), true, true, true, true, grantedAuthorities);
            return myUserDetails;
            
        } else {
            throw new UsernameNotFoundException(eposta);
        }
        
    }
}

Bir sonraki faydalı sınıf Md5PasswordEncoder'dır ; Bu hali hazırda nazır olan bir Spring iç sınıfdır.

Vee...

MyAuthenticationProvider.java : Bu sınıfın amacı kullanıcı onaylamaktır. Ya devam ya tamam denilen kısım burasıdır. Geri kalan detayları atlıyoruz.


public class MyAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

 ........

    @Override
    protected UserDetails retrieveUser(String eposta, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        UserDetails loadedUser;
        loadedUser = this.getUserDetailsService().loadUserByUsername(eposta);

        if ( md5Encoder.isPasswordValid(loadedUser.getPassword(), authentication.getCredentials().toString(), null)) {
            return loadedUser;  // if it comes from directly from user
        }  else if (loadedUser.getPassword().equals(authentication.getCredentials().toString())) {
            return loadedUser; // if it comes from cookie
        }
        throw new BadCredentialsException("Invalid Password");
    }  

 ......
}

D - 

 
        
        
 


Authentication manager; sağlayıcı/lar a (provider) ihtiyaç duyar; bizde burada istenilen 2 adet sağlayıcıyı verdik. Bu sağlayıcılardan bir tanesi form üzerinden yapılan kullanıcı kontrol işlemleri için (myAuthenticationProvider), diğeri ise çerez tabanlı kontrol için gerekli olan sağlayıcıdır (rememberMeAuthenticationProvider). Bu tanımlama sadece Spring 3.x ve üzeri sürümler için geçerlidir.



E - 


 
            
        
        
        
        
        

    

    
    
        
        
        
    

    
    
                class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
        
        
        
        
    


Yukaridaki 3 ayar bölümünün amacı, kullanıcın hareketlerini yakalamaktır. Örneğin kullanıcı giriş yaptığı an, o anı yakalayıp birşey yapmak istersek geçerli olacaktır (örneğin giriş yapan kullanıcıları saymak gibi ). Gerekli açıklamaları üzerlerine yazmaya çalıştım.


F - 


 
            "com.foo.datasource.MyRemembermeAuthenticationProvider">
        
        
 

Benim yazmış olduğum hatırla beni servisini MyRemembermeAuthenticationProvider; Spring sistemine tanıtıyorum.


MyRemembermeAuthenticationProvider.java : Bu sınıfın amacı; hatırla beni (remember-me) mekanizmasına, kullanıcıyı nasıl onaylayacağını göstermektir. Bizim zaten hali hazırda kullanıcıyı onaylama sistemimiz olduğundan, direk fazladan birşey yazmadık.



......
@Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        
        authentication = authenticationManager.authenticate
                (new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword()));

        SecurityContextHolder.getContext().setAuthentication(authentication);


        return authentication;   
    }
......


G -

Sistemden çıkış için gerekli olan tanımlama. 


    
        
        
        
        
            
                
                
                
                
            
        
    


Conclusion : Sizlere  Wicket + Spring Security birleştirilmesini anlatmaya çalıştım. Tam bu arada Apache Shiro projesi 1.0 resmi sürümlerini yayınladılar.  Apache Shiro hayatımda gördüğüm en basit ve kullanışlı Java güvenlik sistemidir. Apache Shiro'da bir şans vermenizi şiddetle öneririm.

Sevgiler.

Altuğ Bilgin ALTINTAŞ.

References: 

Source codes can be reachable from  (It's a maven project): 

Örneği çalıştırmak için, kodları bilgisayarınıza indirin ve konsoldan

> mvn jetty:run

sonra tarayıcıdan gidip : localhost:8080 yazıp sistemi test edebilirsiniz.

Not : İngilizce okumak isteyenler için : http://altuga.wordpress.com/2010/06/05/apache-wicket-spring-security-with-remember-me-option/#comments





Yorum

Hocam Java'yı sizden öğrendik, sizin sâyenizde öğrenimi devâm ettiriyoruz.
Elinize sağlık, mükemmel bir makâle olmuş.
Esen olsun.



ya da
CAPTCHA Images