20. [dbproduitscategories] veritabanına erişim sağlayan web hizmetinin güvenliği
20.1. Çalışma ortamının kurulması
Aşağıdaki projelerle web hizmetinin güvenliğini sağlayacağız:
![]() |
- [spring-security-*] projeleri, [<exemples>\spring-database-generic\spring-security] klasöründe bulunacaktır;
- SGBD ve MySQL için güvenlik, önce [DAO / JDBC] katmanı, ardından [DAO / JPA / Hibernate] katmanı ile sağlanacaktır;
- Alt-F5 tuşlarına basın ve ardından tüm Maven projelerini yeniden oluşturun;
[dbproduitscategories] veritabanında kullanıcılar oluşturmamız gerekiyor. Bunun için [spring-security-create-users-hibernate-eclipselink] çalıştırma yapılandırmasını kullanın:
![]() | ![]() |
Bu yapılandırmanın çalıştırılması, [dbproduitscategories] tablosundan [USERS, ROLES, USERS_ROLES] tablosuna verileri doldurur:
![]() |
![]() |
Oluşturulan [login/passwd] kimlikleri şunlardır: [admin/admin], [user/user], [guest/guest]. Veritabanında şifreler şifrelenmiştir.
![]() |

Bu işlem tamamlandıktan sonra, güvenli web hizmetini başlatan [spring-security-server-jpa-generic-hibernate-eclipselink] adlı çalıştırma yapılandırmasını çalıştırın (MySQL'in başlatılmış olması gerekir):
![]() | ![]() |
Ardından, güvenli web hizmetini test eden [spring-security-client-generic-JUnitTestDao] adlı yürütme yapılandırmasını çalıştırın:
![]() | ![]() |
Test başarılı olmalıdır.
20.2. Eclipse projesi [spring-security-server-jdbc-generic]
Güvenli web hizmeti, [spring-security-server-jdbc-generic] projesi tarafından uygulanmaktadır:
![]() |
Yukarıda:
- [DAO1] katmanı, [dbproduitscategories] veritabanındaki [PRODUITS] ve [CATEGORIES] tablolarını yöneten [DAO] katmanıdır. Bu katman daha önce yazılmıştır;
- [DAO2] katmanı, [DAO] katmanıdır ve bu katman, [USERS], [ROLES] ve [USERS_ROLES] tablolarını yöneten [dbproduitscategories] veritabanındaki tabakadır. Henüz yazılmamıştır;
![]() |
20.2.1. Maven yapılandırması
[spring-security-server-jdbc-generic] projesi, aşağıdaki [pom.xml] dosyası ile yapılandırılmış bir Maven projesidir:
<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>dvp.spring.database</groupId>
<artifactId>spring-security-server-jdbc-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-server-jdbc-generic</name>
<description>démo spring security</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- web sunucusu / jSON -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-webjson-server-jdbc-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<!-- eklentiler -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 29-33. satırlar: İncelenen web hizmeti / json / jdbc arşiviyle mevcut yapı devralınır;
- 24-27. satırlar: Spring Security sınıflarını getiren bağımlılık;
Sonuç olarak, proje Eclipse'e yüklenen diğer projelere ilişkin aşağıdaki bağımlılıkları göstermektedir:
![]() |
20.2.2. [DAO2] katmanı
![]() |
Yukarıda:
- [DAO1] katmanı, [dbproduitscategories] veritabanındaki [PRODUITS] ve [CATEGORIES] tablolarını yöneten [DAO] katmanıdır. Bu katman daha önce yazılmıştır;
- [DAO2] katmanı, [DAO] katmanıdır ve bu katman, [dbproduitscategories] veritabanındaki [USERS], [ROLES] ve [USERS_ROLES] tablolarını yöneten [dbproduitscategories] veritabanındaki tabakadır. Şimdi yazacağımız tabaka budur;
![]() |
Spring Security, aşağıdaki [UsersDetail] arayüzünü uygulayan bir sınıfın oluşturulmasını zorunlu kılar:
![]() |
Bu arayüz, burada [AppUserDetails] sınıfı tarafından uygulanmaktadır:
package spring.security.dao;
import generic.jdbc.config.ConfigJdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import spring.jdbc.entities.Role;
import spring.jdbc.entities.User;
import spring.jdbc.infrastructure.DaoException;
public class AppUserDetails implements UserDetails {
private static final long serialVersionUID = 1L;
// JdbcTemplate
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
// özellikler
private User user;
private String simpleClassName = getClass().getSimpleName();
// oluşturucular
public AppUserDetails() {
}
public AppUserDetails(User user, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.user = user;
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
// -------------------------arayüz
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : getRoles(user.getId())) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
...
// özel yöntemler----------------
private List<Role> getRoles(Long id) {
try {
// kullanıcıyı kimliği üzerinden aranıyor
return namedParameterJdbcTemplate.query(ConfigJdbc.SELECT_ROLES_BYUSERID, Collections.singletonMap("id", id), new ShortRoleMapper());
} catch (Exception e) {
//e.printStackTrace();
throw new DaoException(167, e, simpleClassName);
}
}
}
// --------------------- eşleştiriciler
class ShortRoleMapper implements RowMapper<Role> {
@Override
public Role mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Role(rs.getLong("r_ID"), rs.getLong("r_VERSIONING"), rs.getString("r_NAME"));
}
}
- 22. satır: [AppUserDetails] sınıfı, [UserDetails] arayüzünü uygular;
- 29-30. satırlar: Sınıf, bir kullanıcıyı (19. satır) ve bu kullanıcının ayrıntılarını almayı sağlayan depoyu (20. satır) kapsüller;
- 27. satır: Veritabanına, [spring-jdbc-generic-04] projesinde tanımlanan [NamedParameterJdbcTemplate namedParameterJdbcTemplate] nesnesi aracılığıyla JDBC üzerinden erişilecektir. Bu nesnenin, sıkça yapıldığı gibi Spring tarafından enjekte edilmediğine dikkat edilmelidir. Nesne, 36-39. satırlardaki oluşturucuya sağlanır. Neden? Çünkü [AppUserDetails] sınıfı bir Spring bileşeni değildir (@Component anotasyonu bulunmamaktadır) ve bu nedenle bu sınıfa enjeksiyon yapılamaz;
- 36-39. satırlar: Sınıfı bir kullanıcı ve onun deposuyla örneklendiren yapıcı;
- 42-49. satırlar: [UserDetails] arayüzünün [getAuthorities] yönteminin uygulaması. Bu yöntem, [GrantedAuthority] türünde veya türevinde öğelerden oluşan bir koleksiyon oluşturmalıdır. Burada, 29. satırdaki kullanıcının rollerinden birinin adını kapsayan türetilmiş [SimpleGrantedAuthority] türünü (46. satır) kullanıyoruz;
- 45-47. satırlar: 29. satırdaki kullanıcının rol listesi taranarak [SimpleGrantedAuthority] türünde bir öğe listesi oluşturulur;
- 45. satır: Kullanıcının rollerini elde etmek için 53. satırdaki [getRoles] özel yöntemi kullanılır;
- satır 56: Aşağıdaki SQL [ConfigJdbc.SELECT_ROLES_BYUSERID] komutunu ([Configjdbc] içinde tanımlanmıştır) yürütür:
public static final String SELECT_ROLES_BYUSERID = "SELECT DISTINCT r.ID as r_ID, r.VERSIONING as r_VERSIONING, r.NAME as r_NAME FROM ROLES r, USERS u, USERS_ROLES ur WHERE u.ID=:id AND ur.USER_ID=u.ID AND ur.ROLE_ID=r.ID";
Bu SQL sorgusu, birincil anahtarıyla tanımlanan bir kullanıcının rollerini elde etmek için üç [USERS, ROLES, USERS_ROLES] tablosu arasında bir birleştirme yapar. Sorgu, rolleri aranılan kullanıcının [:id] birincil anahtarıyla parametrelendirilir.
- 56. satır: [SELECT] sorgusunun her bir sonuç satırı, 66-72. satırlardaki [ShortRowMapper] sınıfı aracılığıyla [Role] varlığına dönüştürülür;
[AppUserDetails] sınıfının koduna geri dönelim:
package spring.security.dao;
...
public class AppUserDetails implements UserDetails {
private static final long serialVersionUID = 1L;
// JdbcTemplate
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
// özellikler
private User user;
private String simpleClassName = getClass().getSimpleName();
// yapıcılar
public AppUserDetails() {
}
public AppUserDetails(User user, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.user = user;
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
// -------------------------arayüz
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : getRoles(user.getId())) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getLogin();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
// alıcı ve ayarlayıcılar
...
}
- 35-37. satırlar: [UserDetails] arayüzünün [getPassword] yöntemini uygular. 12. satırdaki kullanıcının şifresi döndürülür;
- satır 39-42: [UserDetails] arayüzünün [getUserName] yöntemini uygular. 12. satırdaki kullanıcının kullanıcı adı döndürülür;
- 44-47. satırlar: kullanıcının hesabı hiçbir zaman sona ermez;
- 49-52. satırlar: Kullanıcının hesabı hiçbir zaman engellenmez;
- 54-57. satırlar: Kullanıcının kimlik bilgileri hiçbir zaman geçerliliğini yitirmez;
- satır 59-62: kullanıcının hesabı her zaman aktiftir;
Spring Security ayrıca [AppUserDetailsService] arayüzünü uygulayan bir sınıfın varlığını zorunlu kılar:
![]() |
Bu arayüz, aşağıdaki [AppUserDetailsService] sınıfı tarafından uygulanır:
package spring.security.dao;
import generic.jdbc.config.ConfigJdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import spring.jdbc.entities.User;
import spring.jdbc.infrastructure.DaoException;
@Service
public class AppUserDetailsService implements UserDetailsService {
// enjeksiyonlar
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
// yerel
private String simpleClassName = getClass().getSimpleName();
@Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
List<User> users;
try {
// kullanıcı, kullanıcı adı ile aranıyor
users = namedParameterJdbcTemplate.query(ConfigJdbc.SELECT_USER_BYLOGIN,
Collections.singletonMap("login", login), new ShortUserMapper());
} catch (Exception e) {
throw new DaoException(145, e, simpleClassName);
}
// bulundu mu?
if (users.size() == 0) {
throw new UsernameNotFoundException(String.format("login [%s] inexistant", login));
}
// kullanıcının ayrıntılarını gösteriyoruz
return new AppUserDetails(users.get(0), namedParameterJdbcTemplate);
}
}
// --------------------- eşleştiriciler
class ShortUserMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return new User(rs.getLong("u_ID"), rs.getLong("u_VERSIONING"), rs.getString("u_NAME"), rs.getString("u_LOGIN"),
rs.getString("u_PASSWORD"));
}
}
- 21. satır: sınıf bir Spring bileşeni olacaktır;
- 25-26. satırlar: Veritabanına, [spring-jdbc-generic-04] projesinin bean'lerinde tanımlanan [NamedParameterJdbcTemplate namedParameterJdbcTemplate] nesnesi aracılığıyla JDBC kullanılarak erişilecektir;
- satır 31-49: [UserDetailsService] arayüzünün (satır 22) [loadUserByUsername] yönteminin uygulanması. Parametre, kullanıcının giriş adıdır;
- satır 36-37: Kullanıcı, kullanıcı adı ile aranır. SQL [ConfigJdbc.SELECT_USER_BYLOGIN] emri şu şekildedir:
public static final String SELECT_USER_BYLOGIN = "SELECT u.ID as u_ID, u.VERSIONING as u_VERSIONING, u.NAME as u_NAME,u.LOGIN as u_LOGIN,u.PASSWORD as u_PASSWORD FROM USERS u WHERE u.LOGIN= :login";
SELECT tarafından döndürülen her satır, 52-58. satırlardaki [ShortUserMapper] sınıfı tarafından [User] varlığına dönüştürülür.
- 42-44. satırlar: Eğer bulunamazsa, bir istisna atılır;
- satır 46: bir [AppUserDetails] nesnesi oluşturulur ve döndürülür. Bu nesne, [UserDetails] türündedir (satır 32). Oluşturucusuna iki bilgi aktarılır:
- bulunan kullanıcı;
- [AppUserDetails] sınıfının veritabanına sorgu göndermesini sağlayacak [namedParameterJdbcTemplate] nesnesi;
20.2.3. [web] katmanı
![]() |
[spring-security-server-jdbc-generic] projesi, [spring-webjson-server-jdbc-generic] projesine bağımlıdır:
![]() |
[web] katmanını uygulayan bu projedir. Bu katmanın değiştirilmesine gerek yoktur.
20.2.4. Projenin güvenlik yapılandırması
![]() |
Proje, aşağıdaki [AppConfig] sınıfı ile yapılandırılmıştır:
1 ![]() |
Daha önce bir Spring Security yapılandırma sınıfıyla karşılaşmıştık (bkz. paragraf 19.2.4):
package hello;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/home").permitAll().anyRequest().authenticated();
http.formLogin().loginPage("/login").permitAll().and().logout().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
Aynı yaklaşımı izleyeceğiz:
- 11. satır: [WebSecurityConfigurerAdapter] sınıfını genişleten bir sınıf tanımlayın;
- 13. satır: Web hizmetinin çeşitli URL öğelerine erişim haklarını tanımlayan bir [configure(HttpSecurity http)] yöntemi tanımlayın;
- 19. satır: kullanıcıları ve rollerini tanımlayan [configure(AuthenticationManagerBuilder auth)] yöntemini tanımlayalım;
[AppConfig] sınıfı şu şekilde olacaktır:
package spring.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import spring.security.dao.AppUserDetailsService;
import spring.webjson.server.config.WebConfig;
@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = { "spring.security.dao", "spring.security.service" })
@Import({ spring.webjson.server.config.AppConfig.class, WebConfig.class })
public class AppConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AppUserDetailsService appUserDetailsService;
// güvenlik
private boolean activateSecurity = true;
@Override
protected void configure(AuthenticationManagerBuilder registry) throws Exception {
// kimlik doğrulama [appUserDetailsService] bean'i tarafından yapılır
// şifre, BCrypt hash algoritmasıyla şifrelenir
registry.userDetailsService(appUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// CSRF
http.csrf().disable();
// güvenli uygulama mı?
if (activateSecurity) {
// şifre, Authorization: Basic xxxx başlığıyla iletilir
http.httpBasic();
// HTTP OPTIONS yöntemi herkes için yetkilendirilmelidir
http.authorizeRequests() //
.antMatchers(HttpMethod.OPTIONS, "/", "/**").permitAll();
// uygulamayı yalnızca ADMIN rolü kullanabilir
http.authorizeRequests() //
.antMatchers("/", "/**") // tüm URL
.hasRole("ADMIN");
// oturum açılmış olsun ya da olmasın?
//http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
}
- 17. satır: Sınıf, bir Spring yapılandırma sınıfıdır;
- 18. satır: Spring Security öğelerini etkinleştirir;
- 26. satır: [DAO2] katmanındaki Spring bileşenleri ve daha sonra ele alacağımız [spring.security.service] paketindeki bileşenler alınır;
- 23. satır: [web] katmanını uygulayan [spring-webjson-server-jdbc-generic] projesinin bean'leri içe aktarılır. Bu bean'ler arasında [DAO1] katmanına ait olanlar da bulunmaktadır;
- 22-23. satırlar: Uygulamanın kullanıcılarına erişim sağlayan [AppUserDetails] sınıfı enjekte edilir;
- 26. satır: Web uygulamasını güvenli hale getiren (true) veya güvenli hale getirmeyen (false) bir boole değeri;
- 28-33. satırlar: [configure(HttpSecurity http)] yöntemi, kullanıcıları ve rollerini tanımlar. Bu yöntem, parametre olarak [AuthenticationManagerBuilder] türünü alır. Bu parametreye iki bilgi eklenir (32. satır):
- 23. satırdaki, kayıtlı kullanıcılara erişim sağlayan [appUserDetailsService] hizmetine bir referans. Burada, kullanıcıların bir veritabanında kayıtlı oldukları bilgisinin görünmediğine dikkat edilmelidir. Dolayısıyla, bunlar bir önbellekte bulunabilir, bir web hizmeti tarafından sağlanabilir, ...
- şifre için kullanılan şifreleme türü. BCrypt algoritmasını kullandık;
- 35-53. satırlar: [configure(HttpSecurity http)] yöntemi, web hizmetinin URL'lerine erişim haklarını tanımlar;
- 38. satır: Giriş projesinde, Spring Security'nin varsayılan olarak bir CSRF (Cross Site Request Forgery) jetonunu yönettiğini ve kimlik doğrulaması yapmak isteyen kullanıcının bu jetonu sunucuya geri göndermesi gerektiğini görmüştük. Burada bu mekanizma devre dışı bırakılmıştır. Buna boole değeri (isSecured=false) eklendiğinde, web uygulaması güvenlik olmadan kullanılabilir;
- 42. satır: HTTP başlığıyla kimlik doğrulama modu etkinleştirilir. İstemci, aşağıdaki HTTP başlığını göndermelidir:
burada code, "login:password" dizesinin Base64 algoritmasıyla kodlanmış halidir. Örneğin, admin:admin dizesinin Base64 kodlaması YWRtaW46YWRtaW4= şeklindedir. Dolayısıyla, kullanıcı adı [admin] ve şifre [admin] olan kullanıcı, kimlik doğrulaması için aşağıdaki HTTP başlığını gönderecektir:
- 47-49. satırlar: Web hizmetindeki tüm URL öğelerinin, [ROLE_ADMIN] rolüne sahip kullanıcılar tarafından erişilebilir olduğunu belirtir. Bu, bu role sahip olmayan bir kullanıcının web hizmetine erişemeyeceği anlamına gelir;
- 51. satır: [session] modunda, bir kez kimlik doğrulaması yapmış bir kullanıcının sonraki erişimlerinde kimlik doğrulaması yapmasına gerek yoktur. Bu, Spring Security'nin varsayılan ayarıdır. 51. satır bu modu devre dışı bırakır. Bu mod etkinse, kullanıcı her erişimde kimlik doğrulaması yapmak zorundadır. Oturum olmadan, güvenli web hizmetinin tepki süresi oturum varken olduğundan daha uzundur; bu nedenle 51. satır yorum satırı olarak işaretlenmiştir;
20.2.5. Güvenli web hizmetinin test edilmesi
Web hizmetini Chrome [Advanced Rest Client] istemcisiyle test edeceğiz. Kimlik doğrulama için HTTP başlığını belirtmemiz gerekecek:
burada [code], [login:password] dizesinin Base64 kodudur. Bu kodu oluşturmak için [spring-security-create-users] projesindeki aşağıdaki programı kullanabiliriz:
![]() |
package spring.security.helpers;
import org.springframework.security.crypto.codec.Base64;
public class Base64Encoder {
public static void main(String[] args) {
// iki argüman beklenir: kullanıcı adı ve şifre
if (args.length != 2) {
System.out.println("Syntaxe : login password");
System.exit(0);
}
// iki argümanı alır
String chaîne = String.format("%s:%s", args[0], args[1]);
// dizgiyi kodlar
byte[] data = Base64.encode(chaîne.getBytes());
// Base64 kodlamasını görüntüler
System.out.println(new String(data));
}
}
Bu programı [admin admin] argümanlarıyla çalıştırırsak:
![]() |
şu sonucu elde ederiz:
Artık testlere hazırız:
- SGBD ve MySQL çalıştırılmalıdır;
- [PRODUITS] ve [CATEGORIES] tablolarını [spring-jdbc-generic-04-fillDataBase] adlı yürütme yapılandırmasıyla dolduruyoruz:
![]() |
- henüz yapılmadıysa, [USERS, ROLES, USERS_ROLES] tablolarını [spring-security-create-users-hibernate-eclipselink] adlı yürütme yapılandırmasıyla dolduruyoruz:
![]() |
- [spring-security-server-jdbc-generic] adlı yürütme yapılandırmasıyla güvenli web hizmetini başlatırız:
![]() |
Ardından, [Advanced Rest Client] adlı Chrome istemcisiyle tüm kategorilerin uzun sürümünü talep ediyoruz:
![]() |
- [1]'te, uzun kategorilerin URL'ini talep ediyoruz;
- [2]'te, GET yöntemiyle;
- [3]'te, kimlik doğrulama başlığı olarak HTTP'i veriyoruz. [YWRtaW46YWRtaW4=] kodu, [admin:admin] dizesinin Base64 kodlamasıdır;
- [4]'te, HTTP komutunu gönderiyoruz;
Sunucunun yanıtı şöyledir:
![]() |
- [1]'te, HTTP kimlik doğrulama başlığı;
- [2]'te, sunucu jSON yanıtını geri gönderir;
Kategori listesi başarıyla alınır:
![]() |
Şimdi, hatalı bir kimlik doğrulama başlığıyla HTTP isteği göndermeyi deneyelim. Bu durumda yanıt şu şekildedir:
![]() |
- [1]'te: HTTP kimlik doğrulama başlığı;
Aşağıdaki yanıtı alıyoruz:
![]() |
- [2]: web hizmetinin yanıtı;
Şimdi user / user kullanıcısını deneyelim. Bu kullanıcı mevcut ancak web hizmetine erişimi yok. Base64 kodlama programını [user user] argümanlarıyla çalıştırırsak:
![]() |
şu sonucu elde ederiz:
![]() |
- [1] : hatalı HTTP kimlik doğrulama başlığı;
![]() |
- [2]: web hizmetinin yanıtı. Bu, önceki [401 Unauthorized] yanıtından farklıdır. Bu sefer kullanıcı doğru bir şekilde kimlik doğrulaması yapmıştır ancak URL'e erişmek için yeterli haklara sahip değildir;
Güvenli bir web hizmeti artık çalışır durumda.
20.2.6. Bir URL kimlik doğrulama
![]() |
Bir kullanıcının web hizmetine erişim izni olup olmadığını belirlememizi sağlayacak bir URL oluşturacağız. Bunun için aşağıdaki gibi yeni bir MVC [AuthenticateController] denetleyicisi oluşturuyoruz:
package spring.security.service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import spring.webjson.server.service.Response;
@RestController
public class AuthenticateController {
@RequestMapping(value = "/authenticate", method = RequestMethod.GET)
public Response<Void> authenticate() {
return new Response<Void>(0, null, null);
}
}
- 9. satır: [AuthenticateController] sınıfı bir Spring denetleyicisidir. Bu nedenle, URL'leri ortaya koyar. [@RestController] anotasyonu, bu URL'leri işleyen yöntemlerin yanıtlarını müşteriye kendilerinin verdiğini belirtir;
- 11. satır: URL ve [/authenticate] nesnelerini döndürür;
- 12-14. satırlar: Yöntem, boş bir [Response] nesnesi döndürür, ancak [status] değeri 0'dır; bu da hata olmadığını gösterir;
Bu URL ne işe yarar? Bir kullanıcıyı doğrulamak istediğimizde bunu talep edeceğiz. Güvenlik katmanı bu kullanıcıyı kabul etmezse bir istisna döndürdüğünü görmüştük. İşte bir örnek;
[admin:admin] kullanıcısıyla:
![]() | ![]() |
Boş bir yanıt alıyoruz, ancak istisna oluşmuyor.
[user:user] kullanıcısıyla:
![]() | ![]() |
Bir istisna oluştu.
20.2.7. Sonuç
Spring Security için gerekli sınıfların eklenmesi, orijinal web / json projesinde herhangi bir değişiklik yapılmadan gerçekleştirilebildi. Bu çok elverişli durum, veritabanına eklenen üç tablonun mevcut tablolardan bağımsız olmasından kaynaklanmaktadır. Hatta bunları ayrı bir veritabanına bile yerleştirebilirdik. Diğer durumlarda, eklenen tablolar mevcut tablolarla ilişkili olabilir. Bu durumda, mevcut [DAO] katmanının kodu gözden geçirilmelidir.
20.3. Güvenli / jSON web hizmeti için programlanmış bir istemci
Güvenli olmayan / jSON web hizmeti için daha önce bir istemci yazmıştık:
![]() |
Şimdi de güvenli web hizmeti için programlanmış bir istemci oluşturacağız:
![]() |
![]() |
20.3.1. [Client HTTP] katmanı
![]() |
![]() | ![]() |
[Client] sınıfı, güvenli web sunucusu / jSON ile HTTP iletişimini sağlar. Az önce gördüğümüz gibi, bu HTTP iletişiminde, istemci artık bir kimlik doğrulama başlığı göndermelidir; örneğin:
[IClient] arayüzü şu şekilde olur:
package spring.webjson.client.dao;
import org.springframework.http.HttpMethod;
import spring.webjson.client.entities.Credentials;
public interface IClient {
public <T1, T2> T1 getResponse(Credentials credentials,String url, HttpMethod method, int errStatus, T2 body);
}
- 8. satır: [getResponse] yönteminin ilk parametresi artık bir kullanıcının kimlik bilgilerini içeren bir [Credentials] nesnesidir:
package spring.security.client.entities;
public class Credentials {
// özellikler
private String login;
private String password;
// yapıcı
public Credentials() {
}
public Credentials(String login, String password) {
this.login = login;
this.password = password;
}
// alıcı ve ayarlayıcılar
...
}
[IClient] arayüzünü uygulayan [Client] sınıfı şu şekilde değişmiştir:
package spring.security.client.dao;
...
@Component
public class Client implements IClient {
// enjeksiyonlar
@Autowired
protected RestTemplate restTemplate;
@Autowired
protected String urlServiceWebJson;
// yerel
private String simpleClassName = getClass().getSimpleName();
private String getBase64(Credentials credentials) {
// kullanıcı adı ve şifreyi base64 ile kodlar - java 8 gerektirir
String chaîne = String.format("%s:%s", credentials.getLogin(), credentials.getPassword());
return String.format("Basic %s", new String(Base64.getEncoder().encode(chaîne.getBytes())));
}
// genel istek
@Override
public <T1, T2> T1 getResponse(Credentials credentials, String url, HttpMethod method, int errStatus, T2 body) {
// sunucunun yanıtı
ResponseEntity<Response<T1>> response;
try {
// istek hazırlanıyor
RequestEntity<?> request = null;
if (method == HttpMethod.GET) {
HeadersBuilder<?> headersBuilder = RequestEntity.get(new URI(String.format("%s%s", urlServiceWebJson, url))) .accept(MediaType.APPLICATION_JSON);
if (credentials != null) {
headersBuilder = headersBuilder.header("Authorization", getBase64(credentials));
}
request = headersBuilder.build();
}
if (method == HttpMethod.POST) {
BodyBuilder bodyBuilder = RequestEntity.post(new URI(String.format("%s%s", urlServiceWebJson, url))).header("Content-Type", "application/json").accept(MediaType.APPLICATION_JSON);
if (credentials != null) {
bodyBuilder = bodyBuilder.header("Authorization", getBase64(credentials));
}
request = bodyBuilder.body(body);
}
// istek yürütülüyor
response = restTemplate.exchange(request, new ParameterizedTypeReference<Response<T1>>() {
});
} catch (Exception e) {
// istisna kapsülleniyor
throw new DaoException(errStatus, e, simpleClassName);
}
...
}
...
}
- 33-35, 40-42. satırlar: [credentials] kullanıcısı null değilse, kimlik doğrulama başlığı eklenir. Kullanıcının ve şifresinin Base64 kodlaması, 17-21. satırlardaki [getBase64] yöntemi ile sağlanır. Bu yöntemin, JDK 1.8'e ait bir [Base64] sınıfını kullandığına dikkat edilmelidir. HTTP istemcimiz, güvenli olmayan bir web hizmeti ile çalışabilir. Buna, null değerine eşit bir [credentials] değeri aktarılması yeterlidir;
- önceki satırlar dışında kod değişmeden kalır;
20.3.2. [DAO] katmanı
![]() |
20.3.2.1. [IDao] arayüzü
![]() |
[spring-webjson-client-generic] projesinin [IDao] arayüzündeki tüm yöntemler, [Credentials credentials] adlı ek bir parametre alır:
package spring.security.client.dao;
import java.util.List;
import spring.security.client.entities.AbstractCoreEntity;
import spring.security.client.entities.Credentials;
public interface IDao<T extends AbstractCoreEntity> extends IAuthenticate {
// T varlıklarının tam listesi
public List<T> getAllShortEntities(Credentials credentials);
public List<T> getAllLongEntities(Credentials credentials);
// belirli varlıkların listesi - kısa sürüm
public List<T> getShortEntitiesById(Credentials credentials, Iterable<Long> ids);
public List<T> getShortEntitiesById(Credentials credentials, Long... ids);
public List<T> getShortEntitiesByName(Credentials credentials, Iterable<String> names);
public List<T> getShortEntitiesByName(Credentials credentials, String... names);
// belirli varlıklar - uzun sürüm
public List<T> getLongEntitiesById(Credentials credentials, Iterable<Long> ids);
public List<T> getLongEntitiesById(Credentials credentials, Long... ids);
public List<T> getLongEntitiesByName(Credentials credentials, Iterable<String> names);
public List<T> getLongEntitiesByName(Credentials credentials, String... names);
// birden fazla varlığın güncellenmesi
public List<T> saveEntities(Credentials credentials, Iterable<T> entities);
public List<T> saveEntities(Credentials credentials, @SuppressWarnings("unchecked") T... entities);
// tüm varlıkların silinmesi
public void deleteAllEntities(Credentials credentials);
// birden fazla varlığın silinmesi
public void deleteEntitiesById(Credentials credentials, Iterable<Long> ids);
public void deleteEntitiesById(Credentials credentials, Long... ids);
public void deleteEntitiesByName(Credentials credentials, Iterable<String> names);
public void deleteEntitiesByName(Credentials credentials, String... names);
public void deleteEntitiesByEntity(Credentials credentials, Iterable<T> entities);
public void deleteEntitiesByEntity(Credentials credentials, @SuppressWarnings("unchecked") T... entities);
}
- 8. satır: [IDao] arayüzü, aşağıdaki [IAuthenticate] arayüzünü genişletir:
package spring.security.client.dao;
import spring.security.client.entities.Credentials;
public interface IAuthenticate {
// kimlik doğrulama
public void authenticate(Credentials credentials);
}
[IAuthenticate] arayüzü, yalnızca [authenticate] adlı tek bir yönteme sahiptir. Bu yöntem, [Credentials credentials] kullanıcısı güvenli web hizmeti tarafından kabul edilirse hiçbir değer döndürmez (void), aksi takdirde bir istisna atar.
20.3.2.2. [AbstractDao] sınıfı
![]() |
[AbstractDao] sınıfının, kategorilerin URL'lerini yöneten [DaoCategorie] sınıflarının ve ürünlerin URL sınıfını yöneten [DaoProduit] sınıfını yönetir. [spring-webjson-client-generic] projesindeki [AbstractDao] sınıfının tüm yöntemleri, alt sınıfa aktardıkları [Credentials credentials] adlı ek bir parametre alır. İşte bir örnek:
@Override
public List<T1> getShortEntitiesById(Credentials credentials, Iterable<Long> ids) {
// argümanın geçerliliği
List<T1> entities = checkNullOrEmptyArgument(true, ids);
if (entities != null) {
return entities;
}
// sonuç
return getShortEntitiesById(credentials, Lists.newArrayList(ids));
}
- [getShortEntitiesById] yöntemi, [Credentials credentials] parametresini alır (2. satır) ve bunu (9. satır) alt sınıftaki [getShortEntitiesById] yöntemine aktarır;
[AbstractDao] sınıfının iskeleti şu şekildedir:
package spring.security.client.dao;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import spring.security.client.entities.AbstractCoreEntity;
import spring.security.client.entities.Credentials;
import spring.security.client.infrastructure.MyIllegalArgumentException;
import com.google.common.collect.Lists;
public abstract class AbstractDao<T1 extends AbstractCoreEntity> implements IDao<T1> {
@Autowired
private IAuthenticate authenticate;
...
}
- 14. satır: Sınıf, daha önce tanımladığımız [IDao] arayüzünü uygular;
- 16-17. satırlar: [IAuthenticate] arayüzünün bir örneği enjekte edilir. Bu arayüz, aşağıdaki [Authenticate] sınıfı tarafından uygulanır:
package spring.security.client.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import spring.security.client.entities.Credentials;
@Component
public class Authenticate implements IAuthenticate{
@Autowired
protected IClient client;
// doğrulama [credentials,mdp]
public void authenticate(Credentials credentials) {
client.<Void, Void> getResponse(credentials, "/authenticate", HttpMethod.GET, 111, (Void) null);
}
}
- 9. satır: [Authenticate] sınıfı bir Spring bileşenidir;
- 10. satır: [IAuthenticate] arayüzünü uygulayan;
- 11-12. satırlar: güvenli web hizmeti ile iletişim kurmayı sağlayan HTTP istemcisinin enjeksiyonu;
- satır 15-17: arayüzün [authenticate] yönteminin uygulanması;
- satır 16: HTTP GET komutu, URL [/authenticate]'e gönderilir. Bu URL komutunun kullanımı 20.2.6. paragrafında gösterilmiştir. Temel prensibi, [credentials] kullanıcısı bilinmiyorsa veya yeterli haklara sahip değilse çağrının bir istisna ile sonlandırılmasıdır;
[AbstractDao] sınıfı, [IDao] arayüzünün [authenticate] yöntemini aşağıdaki şekilde uygular:
@Autowired
private IAuthenticate authenticate;
@Override
public void authenticate(Credentials credentials) {
authenticate.authenticate(credentials);
}
- 7. satır: İşlem, [Authenticate] sınıfının [authenticate] yöntemine devredilir. Dolayısıyla, [Credentials credentials] kullanıcısı güvenli web hizmeti tarafından kabul edilmezse bir istisna oluşur;
20.3.2.3. [DaoCategorie, DaoProduit] sınıfları
![]() |
[DaoCategorie, DaoProduit] sınıfları, [spring-webjson-server-generic] projesine ait olup, ek olarak [Credentials credentials] parametresini içerir. İşte bir örnek:
@Component
public class DaoCategorie extends AbstractDao<Categorie> {
// enjeksiyonlar
@Autowired
protected ApplicationContext context;
@Autowired
protected IClient client;
@Override
public List<Categorie> getAllShortEntities(Credentials credentials) {
try {
// filtreler jSON
ObjectMapper mapper = context.getBean("jsonMapperShortCategorie", ObjectMapper.class);
// tüm kategorileri al
Object map = client.<List<Categorie>, Void> getResponse(credentials, "/getAllShortCategories", HttpMethod.GET,
202, null);
// kategori listesi List<Kategori>
return mapper.readValue(mapper.writeValueAsString(map), new TypeReference<List<Categorie>>() {
});
} catch (DaoException e1) {
throw e1;
} catch (Exception e2) {
throw new DaoException(221, e2, simpleClassName);
}
}
....
20.3.3. Spring yapılandırması
![]() |
[AppConfig] sınıfı, projenin Spring ortamını yapılandırır. Bu sınıf, [spring-webjson-client-generic] projesindekiyle tek bir ayrıntı dışında aynıdır:
@Configuration
@ComponentScan({ "spring.security.client.dao" })
public class AppConfig {
- 2. satır: yeni katmanın paket adı [DAO] olarak girilmelidir;
20.3.4. [DAO] katmanının testleri
![]() |
![]() |
20.3.4.1. [JUnitTestCredentials] testi
[JUnitTestCredentials] testi, belirli kullanıcıların geçerliliğini kontrol etmek için [IDao.authenticate] yöntemini kullanır:
package client.tests.junit;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring.security.client.config.AppConfig;
import spring.security.client.dao.IAuthenticate;
import spring.security.client.entities.Credentials;
import spring.security.client.infrastructure.DaoException;
@SpringApplicationConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class JUnitTestCredentials {
// katman [DAO]
@Autowired
private IAuthenticate authenticate;
// kullanıcılar
static private Credentials admin;
static private Credentials user;
static private Credentials unknown;
@BeforeClass
public static void init() {
admin = new Credentials("admin", "admin");
user = new Credentials("user", "user");
unknown = new Credentials("x", "y");
}
@Test()
public void checkUserUser() {
DaoException se = null;
try {
authenticate.authenticate(user);
} catch (DaoException e) {
se = e;
System.out.println("checkUserUser: " + e);
}
Assert.assertNotNull(se);
Assert.assertEquals("403 Forbidden", se.getExceptions().get(0).getErrorMessage());
}
@Test()
public void checkUserUnknown() {
DaoException se = null;
try {
authenticate.authenticate(unknown);
} catch (DaoException e) {
se = e;
System.out.println("checkUserUnknown : " + e);
}
Assert.assertNotNull(se);
Assert.assertEquals("401 Unauthorized", se.getExceptions().get(0).getErrorMessage());
}
@Test()
public void checkUserAdmin() {
DaoException se = null;
try {
authenticate.authenticate(admin);
} catch (DaoException e) {
se = e;
System.out.println("checkUserAdmin : " + e);
}
Assert.assertNull(se);
}
}
- Test sınıfının başlatılması sırasında (29-34. satırlar), üç kullanıcı oluşturulur:
- [admin] kullanıcısı, web hizmetinin URL'lerine erişebilir. Bu, 63-72. satırlarda test edilir;
- [user] kullanıcısı mevcuttur ancak web hizmetindeki URL'i kullanma yetkisi yoktur. Bu kullanıcı 37-47. satırlarda test edilmektedir;
- [unknown] kullanıcısı mevcut değildir. Bu durum 50-60. satırlarda test edilmiştir;
[spring-security-server-jdbc-generic] [1] adlı yürütme yapılandırmasıyla güvenli web hizmeti başlatılıyor:
![]() |
Ardından, JUnit [JUnitTestCredentials] testi, [spring-security-client-generic-JUnitTestCredentials] [2] yürütme yapılandırmasıyla başlatılır. Elde edilen konsol sonuçları şunlardır:
ve test başarılı oldu:
![]() |
20.3.4.2. [JUnitTestDao] testi
[JUnitTestDao] testi, güvenli olmayan [spring-webjson-client-generic] projesindekiyle aynıdır; tek farkartık test edilen [DAO] katmanındaki yöntemlerin hepsinin ilk parametresi [admin / admin] kullanıcısıdır:
@SpringApplicationConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class JUnitTestDao {
// Spring bağlamı
@Autowired
private ApplicationContext context;
// katman [DAO]
@Autowired
private IDao<Produit> daoProduit;
@Autowired
private IDao<Categorie> daoCategorie;
....
// kullanıcılar
static private Credentials admin;
@BeforeClass
public static void init() {
admin = new Credentials("admin", "admin");
}
@Before
public void clean() {
// her testten önce veritabanı temizlenir
log("Vidage de la base de données", 1);
// [CATEGORIES] tablosu ve buna bağlı olarak [PRODUITS] tablosu boşaltılır
daoCategorie.deleteAllEntities(admin);
// sözlükler boşaltılır
for (Long id : mapCategories.keySet()) {
mapCategories.remove(id);
}
for (Long id : mapProduits.keySet()) {
mapProduits.remove(id);
}
}
private List<Categorie> fill(int nbCategories, int nbProduits) {
// tablolar doldurulur
...
// kategori ekleniyor - buna bağlı olarak ürünler de
// eklenecek - sonuç aynı anda döndürülür
return daoCategorie.saveEntities(admin, categories);
}
private Object[] showDataBase() throws BeansException, JsonProcessingException {
// kategori listesi
log("Liste des catégories", 2);
List<Categorie> categories = daoCategorie.getAllShortEntities(admin);
affiche(categories, context.getBean("jsonMapperShortCategorie", ObjectMapper.class));
// ürün listesi
log("Liste des produits", 2);
List<Produit> produits = daoProduit.getAllShortEntities(admin);
affiche(produits, context.getBean("jsonMapperShortProduit", ObjectMapper.class));
// sonuç
return new Object[] { categories, produits };
}
...
Tüm işlemler, güvenli web hizmetine erişim hakkına yalnızca sahip olan [admin / admin] kullanıcısıyla gerçekleştirilir.
Test, [spring-security-client-generic-JUnitTestDao] adlı yürütme yapılandırmasıyla başlatılacaktır:
![]() |
Test başarılı geçiyor ancak güvenli olmayan web hizmetine kıyasla daha yavaş olduğu görülüyor. Bir uygulamanın güvenli hale getirilmesi, yanıt sürelerini önemli ölçüde artırır. Güvenli web hizmetinin performansında önemli bir faktör göze çarpıyor: bunu yapılandıran [AppConfig] sınıfında şunu yazdık:
@Override
protected void configure(HttpSecurity http) throws Exception {
// CSRF
http.csrf().disable();
// güvenli uygulama mı?
if (activateSecurity) {
// şifre, Authorization: Basic xxxx başlığıyla iletilir
http.httpBasic();
// HTTP OPTIONS yöntemi herkes için yetkilendirilmelidir
http.authorizeRequests() //
.antMatchers(HttpMethod.OPTIONS, "/", "/**").permitAll();
// uygulamayı yalnızca ADMIN rolü kullanabilir
http.authorizeRequests() //
.antMatchers("/", "/**") // tüm URL
.hasRole("ADMIN");
// oturum yok
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
17. satırın bir maliyeti vardır. Bu satır, kullanıcının her erişimde kimlik doğrulaması yapmasını zorunlu kılar ya da kılmaz. Bu satırı yorum satırına alırsak, JUnit testinin süresi belirgin şekilde kısalır, çünkü [admin] kullanıcısı yalnızca ilk test için kimlik doğrulaması yapar, sonraki testler için yapmaz (istemci tarafından HTTP kimlik doğrulama başlığı gönderilse bile, sunucu kullanıcının şifresini yeniden doğrulamaz).
20.4. Eclipse projesi [spring-security-server-jpa-generic]
Güvenli web hizmeti artık, Spring Data JPA ile veritabanına erişimi yöneten [spring-jpa-generic] projesine dayanan [spring-security-server-jpa-generic] projesi tarafından uygulanacaktır:
![]() |
Yukarıda:
- [DAO1] katmanı, [dbproduitscategories] veritabanındaki [PRODUITS] ve [CATEGORIES] tablolarını yöneten [DAO] katmanıdır. Bu katman daha önce yazılmıştır;
- [DAO2] katmanı, [dbproduitscategories] veritabanındaki [USERS], [ROLES] ve [USERS_ROLES] tablolarını yöneten [dbproduitscategories] veritabanına aittir. Bu katman henüz yazılmamıştır;
[spring-security-server-jpa-generic] projesi, öncelikle daha önce incelenen [spring-security-server-jdbc-generic] projesinin kopyalanmasıyla elde edilir. Aslında, [web] ve [security] katmanları değişmez çünkü:
- [DAO1 / Repositories / JPA] katmanı (zaten yazılmış), [DAO1 / JDBC] katmanıyla aynı arayüze sahiptir;
- [DAO2 / Repositories / JPA] katmanı (yazılacak), [DAO2 / JDBC] katmanıyla aynı arayüze sahip olacaktır;
[spring-security-server-jpa-generic] projesi şu şekildedir:
![]() |
- [spring.security.repositories] paketi, [repositories] katmanını uygular;
- [spring.security.dao] paketi, [dao2] katmanını uygular;
20.4.1. Maven Projesi
Proje, aşağıdaki [pom.xml] dosyası ile yapılandırılmış bir Maven projesidir:
<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>dvp.spring.database</groupId>
<artifactId>spring-security-server-jpa-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-server-jpa-generic</name>
<description>démo spring security</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- web sunucusu / jSON -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-webjson-server-jpa-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<!-- eklentiler -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 24-27. satırlar: projenin [security] katmanına olan bağımlılığı;
- 29-33. satırlar: projenin [web] katmanı için bağımlılık. [spring-webjson-server-jpa-generic] projesi, [web] katmanını tamamen uygular. Bu katmanın yazılmasına veya değiştirilmesine gerek yoktur;
Sonuç olarak, bağımlılıklar şu şekildedir:
![]() |
20.4.2. Spring yapılandırması
![]() |
Önceki proje olan [spring-security-server-jdbc-generic]'in [AppConfig] yapılandırma dosyası uygundur. Bu dosyaya sadece ek bir yapılandırma eklenmesi gerekir:
@Configuration
@EnableWebSecurity
@EnableJpaRepositories(basePackages = { "spring.security.repositories" })
@ComponentScan(basePackages = { "spring.security.dao", "spring.security.service" })
@Import({ spring.webjson.server.config.AppConfig.class })
public class AppConfig extends WebSecurityConfigurerAdapter {
- 3. satır: [repositories] katmanını uygulayan paketi bildiriyoruz;
- 4. satır: Spring bean'lerini içeren paketler, yeni projede de aynı adı taşır;
- 5. satır: önceki projede, [spring.webjson.server.config.AppConfig] sınıfı [spring-webjson-server-jdbc-generic] bağımlılığında bulunuyordu. Burada ise [spring-webjson-server-jpa-generic] bağımlılığında bulunacaktır;
20.4.3. JPA katmanı
![]() |
[JPA] katmanı tarafından yönetilen JPA varlıklar, QZXW2HTMLP005281Z projesinin bir bağımlılığı olan [mysql-config-jpa-hibernate] [2] projesinde bulunurQX :
![]() |
[User] sınıfı, [USERS] tablosunun görüntüsüdür:

- ID: birincil anahtar;
- VERSION: satırın sürüm bilgisi sütunu;
- IDENTITY: kullanıcının tanımlayıcı kimliği;
- LOGIN: kullanıcının kullanıcı adı;
- PASSWORD: şifresi;
package generic.jpa.entities.dbproduitscategories;
import generic.jdbc.config.ConfigJdbc;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = ConfigJdbc.TAB_USERS)
public class User implements AbstractCoreEntity {
// özellikler
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = ConfigJdbc.TAB_JPA_ID)
protected Long id;
@Version
@Column(name = ConfigJdbc.TAB_JPA_VERSIONING)
protected Long version;
@Transient
protected EntityType entityType=EntityType.POJO;
// özellikler
@Column(name = ConfigJdbc.TAB_USERS_NAME, length = 30, nullable = false)
private String name;
@Column(name = ConfigJdbc.TAB_USERS_LOGIN, length = 30, unique = true, nullable = false)
private String login;
@Column(name = ConfigJdbc.TAB_USERS_PASSWORD, length = 60, nullable = false)
private String password;
// ilgili UserRole
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = { CascadeType.ALL })
@JsonIgnore
private List<UserRole> userRoles;
// oluşturucular
public User() {
}
public User(Long id, Long version, String identity, String login, String password) {
this.id = id;
this.version = version;
this.name = identity;
this.login = login;
this.password = password;
}
// ------------------------------------------------------------
// [equals] ve [hashcode] yeniden tanımlaması
...
// alıcı ve ayarlayıcılar
...
}
- 23. satır: Sınıf, diğer varlıklar için halihazırda kullanılan [AbstractCoreEntity] arayüzünü uygular;
- satır 34-35: varlığın türü. Bu özellik veritabanında kalıcı olarak kaydedilmez [@Transient];
- satır 38-43: Bir kullanıcının üç temel özelliği (name, login, password);
- satır 46-48: kullanıcının rol listesi. Birden fazla rolü olabilir. Aynı şekilde, bir role birden fazla kullanıcının atanabileceğini de göreceğiz. Dolayısıyla, JPA teriminin anlamı dahilinde, [User] ve [Role] varlıkları arasında bir ilişki vardır:
- bir kullanıcı birden fazla role atıfta bulunabilir;
- bir rol birden fazla kullanıcıya atıfta bulunabilir;
Bu [ManyToMany] ilişkisi, veritabanında [USERS_ROLES] birleştirme tablosu aracılığıyla uygulanır. Bir kullanıcı U'nun bir rol R ile ilişkisi varsa, bu ilişki, varlıkların (U, R) birincil anahtar çifti kaydedilerek [USERS_ROLES] tablosuna eklenir. JPA tarafında ise, [User] ve [Role] varlıklarını birbirine bağlayan [ManyToMany] ilişkisi, iki ayrı [ManyToOne, OneToMany] ilişkisine bölünebilir:
- (devamı)
- [User] varlığından [UserRole] varlığına giden [ManyToOne] ilişkisi;
- [UserRole] varlığından [UserRole] varlığına giden [OneToMany] ilişkisi;
Aynı şekilde, [Role] ve [User] varlıklarını birbirine bağlayan [ManyToMany] ilişkisi, iki [ManyToOne, OneToMany] ilişkisine bölünebilir:
- (devamı)
- [Role] varlığından [UserRole] varlığına giden [ManyToOne] ilişkisi;
- [UserRole] varlığından [User] varlığına giden bir [OneToMany] ilişkisi;
- ilişki 48: Bir kullanıcının birden fazla role sahip olması, [OneToMany] varlıkından [UserRole] varlığına giden bir ilişkiyle ifade edilir;
[Role] sınıfı, [ROLES] tablosunun görüntüsüdür:

- ID: birincil anahtar;
- VERSION: satırın sürüm bilgisi sütunu;
- NAME: rol adı. Varsayılan olarak, Spring Security, ROLE_XX biçimindeki adları bekler; örneğin, ROLE_ADMIN veya ROLE_GUEST;
package generic.jpa.entities.dbproduitscategories;
import generic.jdbc.config.ConfigJdbc;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = ConfigJdbc.TAB_ROLES)
public class Role implements AbstractCoreEntity {
// özellikler
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = ConfigJdbc.TAB_JPA_ID)
protected Long id;
@Version
@Column(name = ConfigJdbc.TAB_JPA_VERSIONING)
protected Long version;
@Transient
protected EntityType entityType=EntityType.POJO;
// özellikler
@Column(name = ConfigJdbc.TAB_ROLES_NAME, length = 30, unique = true, nullable = false)
private String name;
// ilişkili UserRole
@OneToMany(fetch = FetchType.LAZY, mappedBy = "role", cascade = { CascadeType.ALL })
@JsonIgnore
private List<UserRole> userRoles;
// oluşturucular
public Role() {
}
public Role(Long id, Long version, String name) {
this.id = id;
this.version = version;
this.name = name;
}
// alıcı ve ayarlayıcılar
public Role(String name) {
this.name = name;
}
// ------------------------------------------------------------
// [equals] ve [hashcode]'in yeniden tanımlanması
...
// getter ve setter'lar
...
}
- 42-44. satırlar: Bir role birden fazla kullanıcı atanabilmesi, [@OneToMany] varlık ile [UserRole] varlığı arasındaki bir ilişkiyle ifade edilir;
[UserRole] sınıfı, [USERS_ROLES] tablosunun yansımasıdır:

Bir kullanıcı birden fazla role sahip olabilir, bir rol ise birden fazla kullanıcıyı bir araya getirebilir. [USERS_ROLES] tablosu ile somutlaştırılan çok-çok ilişkisi vardır.
- ID: birincil anahtar;
- VERSION: satırın sürüm bilgisi sütunu;
- USER_ID: bir kullanıcının kimliği;
- ROLE_ID: bir rolün kimliği;
package generic.jpa.entities.dbproduitscategories;
import generic.jdbc.config.ConfigJdbc;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;
@Entity
@Table(name = ConfigJdbc.TAB_USERS_ROLES)
public class UserRole implements AbstractCoreEntity {
// özellikler
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = ConfigJdbc.TAB_JPA_ID)
protected Long id;
@Version
@Column(name = ConfigJdbc.TAB_JPA_VERSIONING)
protected Long version;
@Transient
protected EntityType entityType=EntityType.POJO;
// bir UserRole, bir User'a başvurur
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = ConfigJdbc.TAB_USERS_ROLES_USER_ID, nullable = false)
private User user;
// bir UserRole bir Role'a başvurur
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = ConfigJdbc.TAB_USERS_ROLES_ROLE_ID, nullable = false)
private Role role;
// yapıcılar
public UserRole() {
}
public UserRole(User user, Role role) {
this.user = user;
this.role = role;
}
// ------------------------------------------------------------
// [equals] ve [hashcode] yeniden tanımlaması
...
// alıcı ve ayarlayıcılar
...
}
- satır 34-36: [USERS_ROLES] tablosundan [USERS] tablosuna yabancı anahtarı oluşturur;
- satır 38-41: [USERS_ROLES] tablosundan [ROLES] tablosuna yabancı anahtarı oluşturur;
20.4.4. [repositories] katmanı
![]() |
![]() |
[UserRepository] arabirimi, [User] varlıklarına erişimi yönetir:
package spring.security.repositories;
import generic.jpa.entities.dbproduitscategories.Role;
import generic.jpa.entities.dbproduitscategories.User;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Long> {
// kimlik numarasıyla tanımlanan bir kullanıcının rol listesi
@Query("select ur.role from UserRole ur where ur.user.id=?1")
Iterable<Role> getRoles(long id);
// benzersiz giriş adıyla tanımlanan bir kullanıcının rol listesi
@Query("select ur.role from UserRole ur where ur.user.login=?1 and ur.user.password=?2")
Iterable<Role> getRoles(String login, String password);
// kullanıcı adıyla kullanıcı arama
User findUserByLogin(String login);
}
- 9. satır: [UserRepository] arayüzü, Spring Data'nın [CrudRepository] arayüzünü genişletir (7. satır);
- 12-13. satırlar: [getRoles(long id)] yöntemi, [id] ile tanımlanan bir kullanıcının tüm rollerini almayı sağlar
- 16-17. satırlar: Aynı işlem, ancak kullanıcı adı ve şifresiyle tanımlanan bir kullanıcı için geçerlidir;
- 20. satır: bir kullanıcıyı kullanıcı adı ile bulmak için;
[RoleRepository] arayüzü, [Role] varlıklarına erişimi yönetir:
package spring.security.repositories;
import generic.jpa.entities.dbproduitscategories.Role;
import org.springframework.data.repository.CrudRepository;
public interface RoleRepository extends CrudRepository<Role, Long> {
// adına göre bir rolün aranması
Role findRoleByName(String name);
}
- 7. satır: [RoleRepository] arayüzü, [CrudRepository] arayüzünü genişletir;
- 10. satır: Bir rol, adı üzerinden aranabilir. Burada, [Role] varlığının bir [name] alanı olduğunu hatırlatırız. [findEntityByChamp] yöntemi, Spring Data tarafından otomatik olarak uygulanır. Dolayısıyla burada [finRoleByName] yöntemini uygulamaya gerek yoktur. Yalnızca arayüzde bu yöntemi tanımlamak yeterlidir.
[UserRoleRepository] arayüzü, [UserRole] varlıklarına erişimi yönetir:
package spring.security.repositories;
import generic.jpa.entities.dbproduitscategories.UserRole;
import org.springframework.data.repository.CrudRepository;
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {
}
- 7. satır: [UserRoleRepository] arayüzü, [CrudRepository] arayüzünü yeni yöntemler eklemeden genişletmekle yetinir;
20.4.5. [DAO2] katmanı
![]() |
![]() |
[DAO2] katmanında, daha önce 20.2.2. paragrafında incelenen [spring-security-server-jdbc-generic] projesinin [DAO2] katmanında bulunan sınıfların aynısı yer almaktadır. Şimdi bunları, [repositories] katmanındaki sınıfların yardımıyla uygulamak yeterlidir.
[AppUserDetails] sınıfı şu şekilde değişmektedir:
package spring.security.dao;
import generic.jpa.entities.dbproduitscategories.Role;
import generic.jpa.entities.dbproduitscategories.User;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import spring.data.infrastructure.DaoException;
import spring.security.repositories.UserRepository;
public class AppUserDetails implements UserDetails {
private static final long serialVersionUID = 1L;
// özellikler
private User user;
private UserRepository userRepository;
// yerel
private String simpleClassName = getClass().getSimpleName();
// oluşturucular
public AppUserDetails() {
}
public AppUserDetails(User user, UserRepository userRepository) {
this.user = user;
this.userRepository = userRepository;
}
// -------------------------arayüz
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
Iterable<Role> roles;
try {
roles = userRepository.getRoles(user.getId());
} catch (Exception e) {
e.printStackTrace();
throw new DaoException(167, e, simpleClassName);
}
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
...
}
- 31. satırda, sınıfın oluşturucusu ikinci parametre olarak [UserRepository] nesnesini alır; bu nesne, sınıfın belirli bir kullanıcının rollerini almasını sağlar (42. satır);
Spring bileşeni [AppUserDetailsService] ise şu şekilde değişir:
package spring.security.dao;
import generic.jpa.entities.dbproduitscategories.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import spring.data.infrastructure.DaoException;
import spring.security.repositories.UserRepository;
@Service
public class AppUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
// yerel
private String simpleClassName = getClass().getName();
@Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
// kullanıcı, kullanıcı adı ile aranıyor
User user;
try {
user = userRepository.findUserByLogin(login);
} catch (Exception e) {
throw new DaoException(168, e, simpleClassName);
}
// bulundu mu?
if (user == null) {
throw new UsernameNotFoundException(String.format("login [%s] inexistant", login));
}
// kullanıcının ayrıntıları gösteriliyor
return new AppUserDetails(user, userRepository);
}
}
- 18. satır: Hizmetin, 27. satırda kullanıcıyı kullanıcı adıyla tanımlamasını sağlayacak Spring bileşeni [userRepository]'in enjeksiyonu;
Sonuç olarak, sadece [userRepository]'e ihtiyacımız olduğu, diğer iki [roleRepository, userRoleRepository] deposuna ise ihtiyacımız olmadığı anlaşılıyor. Bunlar, [USERS, ROLES, USERS_ROLES] tablolarını doldurmayı amaçlayan bir sonraki projede kullanılacak.
20.4.6. Testler
Güvenli web hizmeti, [spring-security-server-jpa-generic-hibernate-eclipselink] [1] adlı yapılandırma ile başlatılır. Genel istemciye ait [JUnitTestDao] testi, [spring-security-client-generic-JUnitTestDao] ve [2] adlı yapılandırma ile başlatılır:
![]() |
Testler başarılı bir şekilde tamamlandı.
20.5. Eclipse projesi [spring-security-create-users]
![]() |
![]() |
20.5.1. Veritabanı
Projenin çalıştırılması, [dbproduitscategories] tablosundan [USERS, ROLES, USERS_ROLES] tablosuna verileri doldurur:

![]() |
![]() |
Oluşturulan [login/passwd] kimlikleri şunlardır: [admin/admin], [user/user], [guest/guest]. Veritabanında şifreler şifrelenmiştir.
![]() |

20.5.2. Maven Yapılandırması
Proje, aşağıdaki [pom.xml] dosyası ile yapılandırılmış bir Maven projesidir:
<?xml version="1.0" encoding="UTF-8"?>
<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>dvp.spring.database</groupId>
<artifactId>spring-security-create-users-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-security-create-users-jpa</name>
<description>création de utilisateurs dans la base [dbproduitscategories]</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- spring-security-server-jpa-generic -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-security-server-jpa-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 22-25. satırlar: Spring Security çerçevesine bağımlılık. Şifre şifreleme algoritması bu çerçeve tarafından sağlanmaktadır
- 27-31. satırlar: az önce oluşturduğumuz [spring-security-server-jpa-generic] projesine bağımlılık. Bu proje, projenin [repositories] ve [JPA] katmanlarını uygular;
Sonuç olarak, bağımlılıklar şu şekildedir:
![]() |
20.5.3. [console] katmanı
![]() |
[repositories] ve [JPA] katmanları, [spring-security-server-jpa-generic] bağımlılığı tarafından uygulandığından, yalnızca [console] katmanının uygulanması gerekmektedir.
![]() |
- [AppConfig], projenin Spring yapılandırma sınıfıdır;
- [CreateUsers], kullanıcıları ve rolleri oluşturan yürütülebilir sınıftır;
- [Base64Encoder], bir [login, password] çifti için Base64 kodunu oluşturmaya yarayan bir destek sınıfıdır. Bunu daha önce kullanmıştık. Bu proje için gerekli değildir;
Spring yapılandırma sınıfı [AppConfig] şu şekildedir:
package spring.security.install;
import generic.jpa.config.ConfigJpa;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration
@EnableJpaRepositories(basePackages = { "spring.security.repositories" })
@Import({ ConfigJpa.class })
public class AppConfig {
}
- 10. satır: Uygulamanın [repositories] dosyalarının nerede bulunacağı belirtilir. Bunlar, [spring-security-server-jpa-generic] bağımlılığının [spring.security.repositories] paketinde bulunur
- 11. satır: Projenin [JPA] katmanını yapılandıran [ConfigJpa] sınıfının bean'leri içe aktarılır. Bu sınıf, [mysql-config-jpa-hibernate] bağımlılığında bulunur:
![]() |
[CreateUsers] sınıfı şu şekildedir:
package spring.security.install;
import generic.jpa.entities.dbproduitscategories.Role;
import generic.jpa.entities.dbproduitscategories.User;
import generic.jpa.entities.dbproduitscategories.UserRole;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.security.crypto.bcrypt.BCrypt;
import spring.security.repositories.RoleRepository;
import spring.security.repositories.UserRepository;
import spring.security.repositories.UserRoleRepository;
public class CreateUsers {
public static void main(String[] args) {
// son
System.out.println("Travail en cours...");
// üç kullanıcı oluşturuluyor
String[] logins = { "admin", "user", "guest" };
String[] passwds = { "admin", "user", "guest" };
String[] roles = { "admin", "user", "guest" };
// Spring bağlamı
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserRepository userRepository = context.getBean(UserRepository.class);
RoleRepository roleRepository = context.getBean(RoleRepository.class);
UserRoleRepository userRoleRepository = context.getBean(UserRoleRepository.class);
for (int i = 0; i < logins.length; i++) {
// i numaralı kullanıcının bilgilerini alıyoruz
String login = logins[i];
String password = passwds[i];
String roleName = String.format("ROLE_%s", roles[i].toUpperCase());
// rol zaten mevcut mu?
Role role = roleRepository.findRoleByName(roleName);
// mevcut değilse oluşturulur
if (role == null) {
role = roleRepository.save(new Role(roleName));
}
// Kullanıcı zaten mevcut mu?
User user = userRepository.findUserByLogin(login);
// Eğer yoksa, oluşturulur
if (user == null) {
// şifreyi bcrypt ile hash'leriz
String crypt = BCrypt.hashpw(password, BCrypt.gensalt());
// kullanıcı kaydedilir
user = userRepository.save(new User(null, null, login, login, crypt));
// rol ile ilişki oluşturulur
userRoleRepository.save(new UserRole(user, role));
} else {
// kullanıcı zaten mevcutsa, istenen role sahip mi?
boolean trouvé = false;
for (Role r : userRepository.getRoles(user.getId())) {
if (r.getName().equals(roleName)) {
trouvé = true;
break;
}
}
// bulunmazsa, rol ile ilişki oluşturulur
if (!trouvé) {
userRoleRepository.save(new UserRole(user, role));
}
}
}
// Spring bağlamı kapatılıyor
context.close();
// son
System.out.println("Travail terminé...");
}
}
- 22-24. satırlar: üç kullanıcının kullanıcı adı, şifre ve rolünü tanımlar;
- 27. satır: Spring bağlamı, [AppConfig] yapılandırma sınıfından oluşturulur;
- 28-30. satırlar: bir kullanıcı oluşturmak için bize faydalı olabilecek üç [Repository] referansını alır;
- satır 31: üç kullanıcı oluşturulur;
- satır 33-35: i numaralı kullanıcıyı oluşturmak için gerekli bilgiler;
- satır 37: rolün zaten mevcut olup olmadığına bakılır;
- satır 39-41: mevcut değilse, veritabanında oluşturulur. Adı [ROLE_XX] şeklinde olacaktır;
- 43. satır: kullanıcı adı zaten mevcut mu kontrol edilir;
- satır 45-52: kullanıcı adı yoksa, veritabanında oluşturulur;
- 47. satır: Şifre şifrelenir. Burada Spring Security'nin [BCrypt] sınıfı kullanılır (8. satır). Dolayısıyla bu çerçevenin arşivlerine ihtiyaç vardır. [pom.xml] dosyası şu bağımlılığı içerir:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- 49. satır: Kullanıcı veritabanına kaydedilir;
- satır 51: kullanıcıyı rolüne bağlayan ilişki de veritabanına kaydedilir;
- satır 55-60: kullanıcı zaten mevcutsa – bu durumda, atamak istediğimiz rolün mevcut roller arasında olup olmadığına bakılır;
- satır 62-64: aranan rol bulunamazsa, kullanıcıyı rolüyle ilişkilendirmek için [USERS_ROLES] tablosunda bir satır oluşturulur;
- olası istisnalara karşı herhangi bir önlem alınmamıştır. Bu, kullanıcıları hızlı bir şekilde oluşturmak için kullanılan bir destek sınıfıdır;
Projeyi çalıştırmak için [spring-security-create-users-hibernate-eclipselink] adlı çalıştırma yapılandırması çalıştırılacaktır:
![]() |
Az önce iki güvenli web hizmeti oluşturduk:
- biri [security / web / JDBC / MySQL] mimarisiyle;
- diğeri ise [security / web / Hibernate / MySQL] mimarisiyle;
Şimdi diğer iki mimariyi ele alacağız:
- bir [security / web / EclipseLink / SQL Server 2014 Express] mimarisi;
- [security / web / OpenJpa / Oracle Express] mimarisi;
20.5.4. [security / web / EclipseLink / SQL Server] mimarisi
![]() |
![]() |
- [1]'te, bir [JDBC / SQL Server] katmanını ve bir [JPA / EclipseLink / SQL Server] katmanını yapılandıran projeler yüklenir;
Not: Alt-F5 tuşlarına basın ve ardından tüm Maven projelerini yeniden oluşturun.
SGBD SQL Sunucusunun başlatıldığı ve [dbproduitscategories] veritabanının oluşturulduğu varsayılmaktadır. Öncelikle bu veritabanındaki [USERS, ROLES, USERS_ROLES] tablolarını doldurmamız gerekiyor. Bunun için [spring-security-create-users-hibernate-eclipselink] adlı yürütme yapılandırmasını çalıştırın:
![]() | ![]() |
Bu yapılandırma, aşağıdaki üç tabloyu verilerle doldurmalıdır:
![]() | ![]() |
![]() |
![]() |
- [spring-security-server-jpa-generic-hibernate-eclipselink][1] adlı yapılandırma ile güvenli web hizmetini başlatın;
- [spring-security-client-generic-JUnitTestDao][2] adlı yapılandırma ile JUnitTestDao testini başlatın. Testin başarılı olması gerekir: [3];
![]() |
![]() |
20.5.5. [security / web / OpenJpa / Oracle Express] mimarisi
![]() |
![]() |
- [1]'te, bir [JDBC / Oracle Express] katmanını ve bir [JPA / OpenJpa / Oracle Express] katmanını yapılandıran projeler yüklenir;
Not: Alt-F5 tuşlarına basın ve ardından tüm Maven projelerini yeniden oluşturun.
SGBD Oracle Express'in başlatıldığı ve [dbproduitscategories] veritabanının oluşturulduğu varsayılmaktadır. Öncelikle bu veritabanındaki [USERS, ROLES, USERS_ROLES] tablolarını doldurmamız gerekir. Bunun için [spring-security-create-users-openjpa] adlı yürütme yapılandırmasını çalıştırın:
![]() | ![]() |
Bu yapılandırma, aşağıdaki üç tabloyu verilerle doldurmalıdır:
![]() | ![]() |
![]() |
![]() |
- [spring-security-server-jpa-generic-openjpa][1-2] adlı yapılandırma ile güvenli web hizmetini başlatın;
- [spring-security-client-generic-JUnitTestDao][3] adlı yapılandırma ile JUnitTestDao testini başlatın. Testin başarılı olması gerekir: [4];
![]() |
![]() |



























































































