Bu yazıda ise token bazlı doğrulama ve yetkilendirmeyi de görmüş olacağız ve bunu JSON Web Token kullanarak yapacağız. Yazıdaki örneğimiz bir önceki
yazıdaki örneğimizin üstüne eklemeler yapılmış hali olacak, bu nedenle yazının kapsamı içinde olmayan konuları es geçeceğim.
JSON Web Token hakkında bilgi edinmek ya da bilgi tazelemek için Rahman Hocamın şuradaki
yazısını okuyabilirsiniz.
WebSecurityConfig.java
WebSecurityConfig sınıfımız içerisinde sadece configure(HttpSecurity http) metodunda değişiklik yapacağız arkadaşlar.
Metodumuz içerisinde şu değişiklikleri yaptık:
antMatchers(HttpMethod.POST, “/login”).permitAll() ifadesi ile /login path’ine sadece POST isteklerinin yapılabileceğini ve herkese açık olduğunu
söyledik.
addFilterBefore(new JWTLoginFilter(“/login”, authenticationManager()),UsernamePasswordAuthenticationFilter.class) ifadesi ile /login path’ine yapılacak
tüm isteklerin JWTLoginFilter’dan geçmesini sağlıyoruz. JWTLoginFilter bize doğrulanmış kullanıcı için token bilgisi dönecek.
addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) satırı ile de login dışında kalan path’lere yapılacak
isteklerin JWTAuthenticationFilter’dan geçmesini sağlıyoruz. JWTAuthenticationFilter, gelen istekler içerisinde token olup olmadığını kontrol edecek ve
token’dan ilgili bilgileri alıp Spring Security’e aktaracak.
JWTLoginFilter.java
JWTLoginFilter sınıfı WebSecurityConfig sınıfında tanımladığımız üzere /login path’ine gelen istekleri önce attemptAuthentication() metodu ile gelen
username&password’ü vererek Spring Security vasıtası ile doğrulatacak ve gelen user geçerli bir user ise successfulAuthentication() metodu içerisinde
TokenAuthenticationService sınıfındaki addAuthentication() metodunu kullanarak dönülecek response’a bir token eklemesi yapacak.
JWTAuthenticationFilter.java
JWTAuthenticationFilter sınıfı WebSecurityConfig sınıfında da belirtildiği gibi /login path’i dışında kalmış olan tüm path’lere gelen istekleri
denetleyecek olan sınıftır. doFilter() metodu TokenAuthenticationService sınıfındaki getAuthentication metodu ile bir adet Authentication nesnesi alır
ve SecurityContextHolder.getContext().setAuthentication(authentication) kod satırındaki setAuthentication() metoduna bu Authentication nesnesini
parametre olarak geçirir. setAuthentication() metodu kendisine gelen Authentication nesnesi ile
gelen bilgilere sahip user’ın gerçekten olup olmadığı ve yetkisinin ne olduğu durumlarına bakar. Akabinde JWTAuthenticationFilter
filterChain.doFilter() ile red ya da kabul işlemini yaparak görevini tamamlar.
TokenAuthenticationService.java
TokenAuthenticationService sınıfı içerisinde addAuthentication(…) ve getAuthentication(…) metotları yer almakta. addAuthentication(…)
metodu
Authentication nesnesi üzerindeki rolleri concattedRoles String’inde virgüllerle ayrılmış şekilde tutar. Bir sistem üzerindeki bir kullanıcının
birden fazla rolü olabilir, bu nedenle kullanıcıya verilecek token içerisinde kullanıcının sahip olduğu tüm roller de olmalıdır.
JWT string’ini elde ettiğimiz satırda ise setSubject(…) metoduna kullanıcı adını geçiriyoruz.
claim(…) metoduna key-value çifti halinde parametre geçiriyoruz ve kullanıcının sahip olduğu rolleri tutan concattedRoles String’ini
roles key’i ile kaydediyoruz. claim(…) metodunu daha çok token içerisinde gidip gelmesini istediğimiz alanlar için kullanıyoruz.
setExpiration(…) metodu ile token’ın ne kadar süre için geçerli olacağını söylüyoruz. Bizim örneğimizde 10 günlük bir expiration time var.
signWith(…) metoduna bir hash algoritması ve bir de hash sırasında kullanılacak gizli bir anahtar bilgisini geçirerek JWT tokenımızı imzalıyoruz.
Son olarak compact(…) metodu ile de String tiğindeki JWT token’ı elde ediyoruz.
Elde ettiğimiz toke’ı da res.addHeader(…) diyerek HEADER_STRING değişkeninin değeri ile response’a ekliyoruz.
getAuthentication(…) metodu ise,
request nesnesinin header’ından HEADER_STRING değişkenin değeri ile token’ı alıyor.
Eğer token gelmiş ise Claims yani diğer payload kısmından veriler almaya başlıyoruz. Daha önce subject’e koyduğumuz username’i ve roles bilgilerini
alıyoruz. roles nesnesi birden fazla role var ise virgüllerle ayrılmış halde olacağı için öncelikle virgüllerle ayrılmış o string’den bir liste
(roleList) elde ediyoruz ve bir for döngüsü ile GrantedAuthority tipinde veri tutan listeye bu rolleri ekliyoruz.
Son adımda ise username’ti tutan user değişkeni boş değil ise UsernamePasswordAuthenticationToken sınıfının yapılandırıcısına user, null ve rolleri
tutan grantedAuths nesnelerini geçirerek UsernamePasswordAuthenticationToken nesnesini döndürüyoruz.
AccountCredentials.java
AccountCredentials sınıfı da gelen kullanıcı için bilgiler tutacağımız basit bir pojo sınıfı.
Ekran Görüntüleri
Şimdi Postman üzerinden http://localhost:8080/SpringSecurityWithSpringBoot/login adresine body kısmında raw halde aşağıdaki ekran görüntüsünde olan
JSON’ı POST ediyorum ve cevap kısmında bana token dönüşü oluyor:
Bu token ile şimdi de http://localhost:8080/SpringSecurityWithSpringBoot/memberList adresine header içerisinde Authorization key’i ile edindiğim token’ı
bulundurarak GET isteği gönderiyorum. Web servis bana ilgili verileri dönüyor.
Şimdi rolü ROLE_USER olan bir kullanıcı ile aynı işlemi deniyelim.
Gördüğümüz gibi Spring Security role yetkili olmadığı için ilgili kaynağa erişimimi reddediyor.
Bu yazıda anlatacaklarım bu kadar arkadaşlar. Başka bir yazıda görüşene kadar sağlıcakla kalın.