Spring Security
- Spring security ile ilk tanışacaklar için daha önce hazırlamış olduğum
Spring FundamentalsveSpring Bootyazılarımı okumalarını rica ediyorum
Spring Security Nedir ?#
-
Spring Serurityana hatlarıyla bir sürü servlet filtresi içeren bir frameworkdür. -
Aynı zamanda Spring Web MVC ve Spring Boot gibi diğer spring famework leri ile çok uygun bir şekilde çalışmaktadır.
-
Geliştiriciye sunduğu web konfigürasyonları ile oluşturulan uygulamanın tehtitlere karşı güvenli olmasını sağlar.
Bir Web Uygulamasındaki Güvenlik Terimleri#
Spring Security kapsamlı bir şekilde anlamak açısından bilmemiz gereken 3 tane ana konu bulunmaktadır. Bunlar;
- Authentication (Kimlik Doğrulaması)
- Authorization (Yetkilendirme)
- Servlet Filters
Authentication (Kimlik Doğrulaması)#
- Çoğunlukla Web Uygulamaların bir kullanıcı kontrolü yapılmaktadır. Giriş yapmak isteyen kişinin doğruluğunun kontrol edilmesi gerekir. Bu kontrol de genellikle
Kullanıcı adıveŞifreikilisi ile yapılır. - Çok katlı bir binaya girdiğinizi düşünün.
Kimlik Doğrulamasını bu binanın giriş kısmındaki bölüm olarak düşünebilirsiniz.
Authorization (Yetkilendirme)#
- Küçük ve basit uygulamalarda çoğunlukla sadece
Kimlik Doğrulamasıyeterli olacaktır. Kullanıcın kimliği doğrulandıktan sonra kullanıcı uygulamadaki her kısıma ulaşabilmektedir. - Ancak, büyük ölçekli ve kullanıcının erişimi için
İzinveRolbilgisi gerektiren durumlar ortaya çıkacaktır. - Az önce verdiğimiz örnektedi gibi bir binaya girdiğimiz düşünelim.
Kimlik Doğrulamamız yapıldı ancak her kata Erişim imiz yok. İşte bu erişim durumunu bu şekilde düşünebilirsiniz.
Servlet Filter#
- Servlet’i bu başlık altında çok derin olarak anlatmam doğru olmayacaktır.
Ancak, basit ve uygun şekilde basitleştirmek gerekirse; Servlet’ler, Java dünyasında bir web uygulaması oluşturmak için olmazsa olmaz olan bir API’dir.
Uygulamadaki HTTP isteklerini bu servlet’ler kontrol ederler. Spring ise bu Servlet’lere bir implemantasyon hazırlamıştır. Adı da
DispatcherServletdir. Spring bu Core servlet kütüphaneleriniDispatcherServletinterface vasıtasıyla kullanır. - Daha detaylı bilgi için Spring Dökümantasyonu
Hala benimle iseniz devam edelim xd
Peki Servlet Filter Nedir ?
-
Web uygulamalarında kullanıcı adı ve kullanıcı şifresi ya da token bilgileri genellikle
HTTP Headeriçerisinde gönderilirler. Durum böyle olunca sizin oluşturuduğunuz web uygulamasında herhangi bir kimlik doğrulaması işleminde@Controller,@RestContolleranotasyonlu controller larınızda methodlar içerisinde kimlik bilgilerini kontrol etmeniz gerekecek. Ancak şöyle bir durum var ki HTTP isteği çoktan sizin controller’ınıza ulaşmış olacak ve bu senaryo doğru değil. -
Böyle bir kontrol yapmak yerine her istek için, istekler bir filtreden geçip sizin controller’larınıza gelmeli.
-
Java’da böyle bir durumu oluşturabilmemiz
Filter‘lar mevcut. BuFilterı servlet imizin önüne konuçlandırabiliriz.

Örnek bir Filter class’ına bakalım;
import javax.servlet.*;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SecurityServletFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
.
.
.
.
}
-
Yukardaki kod bloğu istenilen filtreyi uygulayacaktır. Ancak şöyle bir durum var ki, uygulama giderek büyüyünce ve daha çeşitli güvenlik kontrollerine ihtiyaç oldukça yeni yeni filtreler ekleme gereksinimi oluşacaktır.
-
Hal böyle olunca, gelen HTTP isteği bir filtreden çıkıp diğer filtreye ve ardından başka bir filtreye geçebilme durumu söz konusu oluyor. Bu duruma
Filter Chainingadı veriliyor.
Örneğin uygulamanızda sırasıyla;
- Kullanıcı için sizin login sayfanıza ulaşıp ulaşamaması için oluşturulan
LoginFilter - Ardından kimlik doğrulaması için
AuthenticationFilter - Ardından da yetki doğrulamaları için
AuhtorizationFilter - En son olarak conttoller’a ulaşan istek.
gibi bir filtre trafiği oluşabilir.
Buraya kadar
Spring Security‘e dair birşey konuşmadık. Ancak yukarıda bahsettiğim ön bilgiler vasıtasıylaSpring Securitynin içinde nasıl çalıştığını anlamanız kolaylaşacaktır.
Spring Security’e Giriş#
-
Spring Bootile oluşturduğunuz bir projeyeSpring Securityentegrasyonu yapıp çalıştırdığınızda aslında arkada,Spring Security‘nin içinde hazır bir şekilde bekleyen yaklaşık 15 taneServletFilteruygulamanıza inject ediliyor. -
Sping Security‘i uygulamanıza dahil etmeniz ve uygulamayı çalıştırdığınız taktirde, aşağıdaki gibi bir spring security logu göreceksiniz
INFO 1503 --- [ restartedMain ] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@791539ac, org.springframework.security.web.context.SecurityContextPersistenceFilter@56cd93ec, org.springframework.security.web.header.HeaderWriterFilter@747ce708, org.springframework.security.web.csrf.CsrfFilter@43213d9b, org.springframework.security.web.authentication.logout.LogoutFilter@6e02dda8, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@29996ad9, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@22be4c29, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@3a84d7ce, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@718584a0, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4b172313, org.springframework.security.web.session.SessionManagementFilter@1ef6540, org.springframework.security.web.access.ExceptionTranslationFilter@742a8e6b, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@c74844]
- Yani gelen bir HTTP isteği 15 adet filtreden geçip sizin
@RestController‘ınıza ulaşıyor.
Bu hazır filtrelerden bazıları şu şekilde;
BasicAuthenticationFilter: Gelen HTTP isteğinin içerisinde Basic Authentication Header‘ı arar
ve bulursa header da verilen username, password ikilisi ile kimlik doğrulaması yapar.
UsernamePasswordAuthenticationFilter: Gelen HTTP isteğinin içerisinde username/password ikilisini, parametrede ya da body’de arar ve bulursa kimlik doğrulaması yapar.
DefaultLoginPageGeneratingFilter: Uygulama için hazır bir login ekranı oluşturur.
DefaultLogoutPageGeneratingFilter: Uygulama için hazır bir logout ekranı oluşturur.
FilterSecurityInterceptor: Authorization (Yetkilendirme) işini halleder.
Sonuç olarak buradaki hazır filtreler uygulama için geçerli olacak güvenlik yeteneklerini getirir. Bu durumda bize kalan da bu filtrelerin konfigürasyonlarını yapmak ve uygulama için bu filtreleri kullanılır hale getirmektir.
Spring Security Konfigürasyonları#
Spring Security‘i projeye entegre etmek için gerekli olan iki ana adım mevcut. Bunlar;
- Konfigürasyon sınıfına
@EnableWebSecurityanotasyonu eklemek. - Konfigürasyon sınıfının
WebSecurityConfigurerAdaptersınıfını extend etmesi gerekmektedir. Bu sınıf sayesinde belirtilen URI üzerinde güvenlik kontrollerini kontrol edebileceksiniz.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll() // (1)
.anyRequest().authenticated() // (2)
.and()
.formLogin() // (3)
.loginPage("/login")
.permitAll()
.and()
.logout() // (4)
.permitAll()
.and()
.httpBasic(); // (5)
}
}
-
/api/auth/**URI’sine gelen tüm istekler Spring Security tarafından servlete iletilecektir. Kullanıcının bu URI’lere erişimi için herhangi bir kimlik doğrulamasına tabi tutulması gerekmez.antMatcherkullandığımız için bu alana wildcard semboller(*, /**) kullanılabilmektir. -
Birinci kuralda belirtilen istek URI’ları dışında doğer gelen tüm istekler Spring Security tarafından kullanıcının kimlik bilgisi olmadan erişime izin verilmeyecektir.
-
Kullanıcı Adı / Şifre ikilisi ile giriş yapılabilecek
/loginURI’ı üzerinde bulunan bir login sayfasına erişim izni veriliyor. Herkes login sayfasına erişim sağlayabilecek. -
Aynı şekilde çıkış sayfası için de herkesin erişimi olacak.
-
Kimlik bilgilerinin HTTP basic header’ı içinde gönderileceği izni.
Spring Security ile Kimlik Doğrulaması#
- Kabaca bir web uygulamasında kimlik doğrulaması yapmak için 3 tane yöntem gösterilebiril. Bunlar;
- Veritabanı ile: Kullanıcının şifrelenmiş bilgilerini (Username / Password) alıp veritabanındaki bilgileri ile karşılaştırmak.
- 3rd Party ile: Kullanıcının şifrelenmiş şifre bilgisine erişiminiz yoktur. 3rd party bir ürün tarafından sağlanan service ile kimlik doğrulaması yapılabilir.
- OAuth2 ile: Google / Twitter / Facebook üzerinden kullanıcın bilgilerinin doğrulaması şeklinde.
Önemli not : Eğer veritabanı ile doğrulama yapmayı seçtiyseniz kullanıcı şifresini kesinlikle ve kesinlikle bir şifreleme yöntemi ile şifrelemeyi unutmayınız.
1. UserDetailsService - Kullanıcının şifresine erişimimiz olduğunda#
- Kullanıcı bilgileriniz veritabanınızda bir tabloda saklandığınızı varsayalım.
| id | username | password | … | … |
|---|---|---|---|---|
| 1 | user | s3cr3t | … | … |
| … | … | … | … | … |
| … | … | … | … | … |
- Bu durumda
Spring Security‘nin düzgün çalışabilmesini sağlamak için iki adet@Bean‘e ihitiyacımız var.
- UserDetailsService
- PasswordEncoder
UserDetailsService bean’ini uygun bulduğunuz bir service nesnenizin içine kolayca tanımlayabilirsiniz.
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl(); // (1)
}
Ardından UserDetailsService nesneini implement edecek olan UserDetailsServiceImpl nesnesine bakalım.
@Service
public class UserDetailServiceImpl implements UserDetailsService {
// Autowire the UserRepository
private final UserRepository userRepository;
public UserDetailServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
// Authenticate a user from the db
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) {
// Find user by his username
// If found, feed the user to the createSpringSecurityUser method
// If not found, throw UsernameNotFoundException exception
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String loginName, User user) {
// return a new org.springframework.security.core.userdetails.User which is mapped by your User class
}
}
Bu implementasyonda önemli olan birkaç durum söz konusu.
- Dikkat edildiği üzere
@Overrideedilen loadUserByUsername methodu sadece username parametresi almakta. Bu durumda biz de sadece username ile bir arama yapmak durumundayız. - Veritabanında bulunan
usernamebilgisi her bir kullanıcı için biricik olmalıdır.