17. [TD]: web sunucusunun güvenliği / jSON seçimler
Anahtar kelimeler: çok katmanlı mimari, Spring, bağımlılık enjeksiyonu, web hizmeti / jSON güvenli, istemci / sunucu
Şimdi, önceki bölümde öğrendiklerimizi seçimler ile ilgili TD'e uygulayacağız. Mimari şu şekilde olacaktır:
![]() |
İşlem şu şekilde ilerleyecektir:
- sunucu kodunun yazılması;
- [ui] katmanı olmadan, ancak [métier] katmanının JUnit testi ile istemci yazımı;
- [ui] katmanı ile istemci yazma;
17.1. Support
![]() | ![]() |
Bu bölümdeki projeler [support / chap-17] klasöründe bulunur. SQL komut dosyası, testler için gerekli veritabanını oluşturmak amacıyla kullanılır.
17.2. Veritabanı
Güvenli sunucudaki veritabanında artık [USERS], [ROLES] ve [USERS_ROLES] tabloları bulunmalıdır:
![]() | ![]() |
Veritabanı oluşturma komut dosyası SQL, belge desteğinde mevcuttur.
17.3. Güvenli sunucu
Seçimler için güvenli sunucuyu kurmak amacıyla, [intro-spring-security-server-01] örnek projesini kopyalayıp yapıştırmanız yeterlidir:
![]() |
Yapılması gereken iş: Paketleri [1]'te gösterildiği gibi yeniden adlandırın.
Bu işlem tamamlandıktan sonra, [pom.xml] dosyasını aşağıdaki şekilde değiştirin:
<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>istia.st.elections</groupId>
<artifactId>elections-security-webjson-metier-dao-spring-data</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elections-security-webjson-metier-dao-spring-data</name>
<description>elections spring security</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>istia.st.elections</groupId>
<artifactId>elections-webjson-metier-dao-spring-data</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Security -->
...
</dependencies>
<!-- eklentiler -->
<build>
...
</build>
</project>
- 23-27. satırlarda, [intro-server-webjson-01] projesine olan bağımlılığı, 12. paragrafta incelenen [elections-webjson-metier-dao-spring-data] projesine olan bağımlılıkla değiştirin;
- 4-7. satırlara yeni projenin özelliklerini girin;
Son olarak, projenin yapılandırma sınıflarını değiştirmemiz gerekiyor:
[DaoConfig]
package elections.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories(basePackages = { "elections.security.repositories" })
@ComponentScan(basePackages = { "elections.security.dao" })
@Import({ elections.dao.config.DaoConfig.class })
public class DaoConfig {
// sabitler
final static private String[] ENTITIES_PACKAGES = { "elections.dao.entities", "elections.security.entities" };
@Bean
public String[] packagesToScan() {
return ENTITIES_PACKAGES;
}
}
Değişiklikler aşağıdaki satırlarda yapılmalıdır:
- 8., 9. ve 14. satırlar: doğru paket adlarını girin;
- 10. satır: İçe aktarılan sınıf artık [elections-webjson-metier-dao-spring-data] projesinden [elections.dao.config.DaoConfig]'tir;
Not: Daha önce de belirtildiği gibi, bu yapılandırma yalnızca [elections.dao.config.DaoConfig] sınıfında [@Configuration] anotasyonu varsa çalışır. Bu noktayı kontrol edin.
[SecurityConfig]
package elections.security.config;
...
@EnableWebSecurity
@ComponentScan(basePackages = { "elections.security.service" })
@Import({ WebConfig.class, DaoConfig.class })
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
Değişiklikler aşağıdaki satırlarda yapılmıştır:
- 6. satır: paket adını değiştirin;
Hepsi bu kadar. JUnit [] testini çalıştırarak ilk testi yapın:
![]() |
[Boot] önyükleme sınıfını çalıştırın, ardından [Advanced Rest Client] Chrome eklentisiyle aşağıdaki testi yapın:
![]() |
[1]'te istek. [3]'te yanıt. [2]'te, HTTP başlığı, [admin, admin] kullanıcısının kimlik doğrulama başlığıdır: Authorization:Basic YWRtaW46YWRtaW4=
Şimdi, yarışmadaki listeleri isteyin:
![]() |
17.4. [ui] katmanı olmayan güvenli sunucu istemcisi
![]() |
[elections-ui-metier-dao-webjson] projesini [elections-metier-dao-security-webjson] projesine kopyalayıp yapıştırın.
![]() |
Yapılması gereken iş: [1]'te, gerekirse paketlerin adını değiştirin ve [ui] katmanındaki paketleri ile [boot] başlangıç sınıflarını silin.
Şimdi 16.5. paragrafında yapıldığı gibi ilerleyeceğiz.
17.4.1. Maven Yapılandırması
Yalnızca projenin tanım kısmı değişiyor:
<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>istia.st.elections</groupId>
<artifactId>elections-metier-dao-security-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>Client jUnit du serveur web / jSON</description>
<name>elections-metier-dao-security-webjson</name>
...
17.4.2. [métier] katmanının yeniden yapılandırılması
![]() |
[IElectionsMetier] arayüzü şu şekilde değişir:
package elections.security.client.metier;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
public interface IElectionsMetier {
// kimlik doğrulama
public void authenticate(User user);
// yarışmadaki listeleri alma
public ListeElectorale[] getListesElectorales(User user);
// doldurulacak koltuk sayısı
public int getNbSiegesAPourvoir(User user);
// seçim barajı
public double getSeuilElectoral(User user);
// sonuçların kaydı
public void recordResultats(User user, ListeElectorale[] listesElectorales);
// koltuk hesaplaması
public ListeElectorale[] calculerSieges(User user, ListeElectorale[] listesElectorales);
}
Tüm yöntemlerin ilk parametresi, güvenli sunucunun kaynaklarını kullanmak isteyen kullanıcıdır.
[ElectionsMetier] uygulaması şu şekilde değişir:
package elections.security.client.metier;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import elections.security.client.dao.IClientDao;
import elections.security.client.entities.ElectionsConfig;
import elections.security.client.entities.ElectionsException;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
@Component
public class ElectionsMetier implements IElectionsMetier {
@Autowired
private IClientDao dao;
@Autowired
private ApplicationContext context;
// seçim yapılandırması
private ElectionsConfig electionsConfig;
private ElectionsConfig getElectionsConfig(User user) {
if(electionsConfig!=null){
return electionsConfig;
}
// haritalayıcılar jSON
ObjectMapper mapperResponse = context.getBean(ObjectMapper.class);
try {
// sorgu
Response<ElectionsConfig> response = mapperResponse.readValue(dao.getResponse(user, "/getElectionsConfig", null),
new TypeReference<Response<ElectionsConfig>>() {
});
// hata mı?
if (response.getStatus() != 0) {
// 1 istisna atılıyor
throw new ElectionsException(response.getStatus(), response.getMessages());
} else {
electionsConfig = response.getBody();
return electionsConfig;
}
} catch (ElectionsException e1) {
throw e1;
} catch (IOException | RuntimeException e2) {
throw new ElectionsException(100, e2);
}
}
@Override
public ListeElectorale[] getListesElectorales(User user) {
...
}
@Override
public int getNbSiegesAPourvoir(User user) {
return getElectionsConfig(user).getNbSiegesAPourvoir();
}
@Override
public double getSeuilElectoral(User user) {
return getElectionsConfig(user).getSeuilElectoral();
}
@Override
public void recordResultats(User user, ListeElectorale[] listesElectorales) {
...
}
@Override
public ListeElectorale[] calculerSieges(User user, ListeElectorale[] listesElectorales) {
...
}
@Override
public void authenticate(User user) {
dao.getResponse(user, "/authenticate", null);
}
}
Yapılması gereken: Yukarıdaki kodu tamamlayın.
17.4.3. Spring yapılandırması
![]() |
[MetierConfig] sınıfı şu şekilde değişir:
package elections.security.client.config;
...
@ComponentScan({ "elections.security.client.dao", "elections.security.client.metier" })
public class MetierConfig {
- satırda, taranacak paketler güncellenmelidir.
17.4.4. [métier] katmanındaki JUnit testi
![]() |
[Test01] test sınıfı şu şekilde değişmektedir:
package elections.security.client.metier.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 com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import elections.security.client.config.MetierConfig;
import elections.security.client.entities.ElectionsException;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import elections.security.client.metier.IElectionsMetier;
@SpringApplicationConfiguration(classes = MetierConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
// katman [electionsMetier]
@Autowired
private IElectionsMetier electionsMetier;
// jSON eşleştiricisi
private final ObjectMapper mapper = new ObjectMapper();
// kullanıcılar
static private User admin;
static private User user;
static private User unknown;
@BeforeClass
public static void initTest() {
admin = new User("admin", "admin");
user = new User("user", "user");
unknown = new User("x", "y");
}
@Test()
public void checkUserUser() {
ElectionsException se = null;
try {
electionsMetier.authenticate(user);
} catch (ElectionsException e) {
se = e;
}
Assert.assertNotNull(se);
Assert.assertEquals("403 Forbidden", se.getErreurs().get(0));
}
@Test()
public void checkUserUnknown() {
ElectionsException se = null;
try {
electionsMetier.authenticate(unknown);
} catch (ElectionsException e) {
se = e;
}
Assert.assertNotNull(se);
Assert.assertEquals("401 Unauthorized", se.getErreurs().get(0));
}
@Test()
public void checkUserAdmin() {
ElectionsException se = null;
try {
electionsMetier.authenticate(admin);
} catch (ElectionsException e) {
se = e;
}
Assert.assertNull(se);
}
/**
* vérification 1 : méthode de calcul des sièges on fixe en dur les listes
*/
@Test
public void calculSieges1() {
// 7 aday listesinden oluşan tablo oluşturulur
ListeElectorale[] listes = new ListeElectorale[7];
listes[0] = new ListeElectorale("A", 32000, 0, false);
listes[1] = new ListeElectorale("B", 25000, 0, false);
listes[2] = new ListeElectorale("C", 16000, 0, false);
listes[3] = new ListeElectorale("D", 12000, 0, false);
listes[4] = new ListeElectorale("E", 8000, 0, false);
listes[5] = new ListeElectorale("F", 4500, 0, false);
listes[6] = new ListeElectorale("G", 2500, 0, false);
// her listenin koltuk sayısı hesaplanır
listes = electionsMetier.calculerSieges(admin,listes);
// sonuçlar kontrol ediliyor
Assert.assertEquals(2, listes[0].getSieges());
Assert.assertFalse(listes[0].isElimine());
Assert.assertEquals(2, listes[1].getSieges());
Assert.assertFalse(listes[1].isElimine());
Assert.assertEquals(1, listes[2].getSieges());
Assert.assertFalse(listes[2].isElimine());
Assert.assertEquals(1, listes[3].getSieges());
Assert.assertFalse(listes[3].isElimine());
Assert.assertEquals(0, listes[4].getSieges());
Assert.assertFalse(listes[4].isElimine());
Assert.assertEquals(0, listes[5].getSieges());
Assert.assertTrue(listes[5].isElimine());
Assert.assertEquals(0, listes[6].getSieges());
Assert.assertTrue(listes[6].isElimine());
}
/**
* vérification 2 : méthode de calcul des sièges on demande les listes à la couche [metier] puis on fixe en dur les
* voix
*/
@Test
public void calculSieges2() {
// 7 aday listesinin tablosu oluşturulur
ListeElectorale[] listes = electionsMetier.getListesElectorales(admin);
// oylar sabitlenir
listes[0].setVoix(32000);
listes[1].setVoix(25000);
listes[2].setVoix(16000);
listes[3].setVoix(12000);
listes[4].setVoix(8000);
listes[5].setVoix(4500);
listes[6].setVoix(2500);
// her bir listenin kazandığı sandalye sayısı hesaplanır
listes = electionsMetier.calculerSieges(admin,listes);
// sonuçlar kontrol ediliyor
Assert.assertEquals(2, listes[0].getSieges());
Assert.assertFalse(listes[0].isElimine());
Assert.assertEquals(2, listes[1].getSieges());
Assert.assertFalse(listes[1].isElimine());
Assert.assertEquals(1, listes[2].getSieges());
Assert.assertFalse(listes[2].isElimine());
Assert.assertEquals(1, listes[3].getSieges());
Assert.assertFalse(listes[3].isElimine());
Assert.assertEquals(0, listes[4].getSieges());
Assert.assertFalse(listes[4].isElimine());
Assert.assertEquals(0, listes[5].getSieges());
Assert.assertTrue(listes[5].isElimine());
Assert.assertEquals(0, listes[6].getSieges());
Assert.assertTrue(listes[6].isElimine());
}
/**
* vérification 3 méthode de calcul des sièges on provoque une exception
*/
@Test(expected = ElectionsException.class)
public void calculSieges3() {
// her biri 1 oy alan 24 aday listesinden oluşan bir tablo oluşturulur
ListeElectorale[] listes = new ListeElectorale[25];
// 25 listenin hepsi aynı sayıda oy alacak (%4)
for (int i = 0; i < listes.length; i++) {
listes[i] = new ListeElectorale("Liste" + (i + 1), 1, 0, false);
}
// sandalye hesaplaması - normalde ElectionsException olmalı
// %5'lik seçim barajı ile
electionsMetier.calculerSieges(admin,listes);
}
/**
* enregistrement des résultats de l'élection
*
* @throws JsonProcessingException
*/
@Test
public void ecritureResultatsElections() throws JsonProcessingException {
// 7 aday listesinin tablosu oluşturulur
ListeElectorale[] listes = electionsMetier.getListesElectorales(admin);
// oylar sabitlenir
listes[0].setVoix(32000);
listes[1].setVoix(25000);
listes[2].setVoix(16000);
listes[3].setVoix(12000);
listes[4].setVoix(8000);
listes[5].setVoix(4500);
listes[6].setVoix(2500);
// her listenin kazandığı sandalye sayısı hesaplanıyor
listes = electionsMetier.calculerSieges(admin,listes);
// sonuçlar görüntülenir
for (int i = 0; i < listes.length; i++) {
System.out.println(mapper.writeValueAsString(listes[i]));
}
// sonuçlar veritabanına kaydedilir
electionsMetier.recordResultats(admin,listes);
// sonuçlar kontrol edilir
listes = electionsMetier.getListesElectorales(admin);
// sonuçlar görüntülenir
for (int i = 0; i < listes.length; i++) {
System.out.println(mapper.writeValueAsString(listes[i]));
}
Assert.assertEquals(2, listes[0].getSieges());
Assert.assertFalse(listes[0].isElimine());
Assert.assertEquals(2, listes[1].getSieges());
Assert.assertFalse(listes[1].isElimine());
Assert.assertEquals(1, listes[2].getSieges());
Assert.assertFalse(listes[2].isElimine());
Assert.assertEquals(1, listes[3].getSieges());
Assert.assertFalse(listes[3].isElimine());
Assert.assertEquals(0, listes[4].getSieges());
Assert.assertFalse(listes[4].isElimine());
Assert.assertEquals(0, listes[5].getSieges());
Assert.assertTrue(listes[5].isElimine());
Assert.assertEquals(0, listes[6].getSieges());
Assert.assertTrue(listes[6].isElimine());
}
}
Yapılması gereken iş: Bu testi anlayın ve başarılı olup olmadığını kontrol edin.
17.5. [console] katmanıyla güvenli sunucunun istemcisi
![]() |
NetBeans ile [elections-metier-dao-security-webjson] ve [elections-ui-metier-dao-webjson] adlı Maven projelerini açıyoruz, ardından [elections-ui-metier-dao-webjson] projesini kopyalayıp yapıştırarak yeni bir [elections-console-metier-dao-security-webjson] projesi oluşturuyoruz:
![]() |
Yeni [3] projesindeki sınıfların çoğu, güvenli olmayan web sunucusu / jSON'in istemcisi olan [elections-ui-metier-dao-webjson] ve [2] projelerinden alınmıştır:
![]() |
Yeni Maven projesini oluşturmak için şu adımları izleyin:
- [elections-ui-metier-dao-webjson] projesini [elections-ui-metier-dao-security-webjson] projesine kopyalayıp yapıştırın;
- yeni projede:
- [elections.client.dao, elections.client.metier, elections.client.entities] paketlerini silin;
- [elections.client.ui] paketinde, yalnızca [ElectionsConsole] konsol sınıfını ve [IElectionsUI] arayüzünü koruyun;
- paketleri belirtildiği gibi [3] olarak yeniden adlandırın;
- [clic droit / Fix Imports] ile çeşitli sınıflardaki içe aktarma sorunlarını giderin;
Bu aşamada, yeni projede çok sayıda hata bulunmaktadır.
17.5.1. Maven Yapılandırması
Yeni projenin [pom.xml] dosyası şu şekildedir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-console-metier-dao-security-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elections-console-metier-dao-security-webjson</name>
<description>couche console du client web / jSON</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>istia.st.elections</groupId>
<artifactId>elections-metier-dao-security-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 4-8. satırlar: yeni projenin Maven kimliği;
- 22-26. satırlar: 17.4. paragrafta az önce oluşturduğumuz ve [DAO] ile [métier] katmanlarını sağlayan [elections-metier-dao-security-webjson] projesine olan bağımlılık;
17.5.2. Spring Yapılandırması
![]() |
[UiConfig] sınıfı şu şekildedir:
package elections.security.client.config;
import elections.security.client.entities.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
@Import(MetierConfig.class)
@ComponentScan(basePackages = {"elections.security.client.console","elections.security.client.swing"})
public class UiConfig {
// yönetici
private final User ADMIN = new User("admin", "admin");
@Bean
private User admin() {
return ADMIN;
}
}
- 8. satır: 17.4. paragrafta incelenen [elections-metier-dao-security-webjson] projesinden [elections.security.client.config.MetierConfig] sınıfı içe aktarılır;
- 9. satır: Spring bean'lerinin bulunduğu paketler tanımlanmaktadır;
- 12-18. satırlar: [admin] bean'i, konsol uygulaması tarafından kullanılacak olan [admin, admin] kullanıcısıdır. Bu kullanıcının, güvenli /jSON web sunucusuna sorgu gönderebilme yetkisine sahip tek kullanıcı olduğunu hatırlayalım;
17.5.3. Konsol önyükleme uygulaması
![]() |
[ElectionsConsole] sınıfı şu şekilde gelişir:
package elections.security.client.console;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import elections.security.client.entities.ElectionsException;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import elections.security.client.metier.IElectionsMetier;
@Component
public class ElectionsConsole implements IElectionsUI {
@Autowired
private IElectionsMetier electionsMetier;
@Autowired
private User admin;
@Override
public void run() {
// yarışmadaki listeler
ListeElectorale[] listes;
// verilerin girilmesi
try (Scanner clavier = new Scanner(System.in)) {
// [metier] katmanından yarışan listeler isteniyor
listes = electionsMetier.getListesElectorales(admin);
// oyların girişi yapılır
System.out.println("Il y a " + listes.length
+ " listes en compétition. Veuillez indiquer le nombre de voix de chacune d'elles :");
for (int i = 0; i < listes.length; i++) {
boolean saisieOK = false;
while (!saisieOK) {
System.out.print("Nombre de voix de la liste [" + listes[i].getNom() + "] : ");
try {
listes[i].setVoix(Integer.parseInt(clavier.nextLine()));
saisieOK = true;
} catch (ElectionsException | NumberFormatException ex) {
System.out.println("Nombre de voix incorrect. Veuillez recommencer");
}
}
}
}
// koltuk hesaplaması yapılıyor
listes=electionsMetier.calculerSieges(admin,listes);
// sonuçlar kaydediliyor
electionsMetier.recordResultats(admin,listes);
// listeler oy sayısına göre azalan sırada sıralanıyor
Arrays.sort(listes, new CompareListesElectorales());
// listeler görüntüleniyor
System.out.println("\nRésultats de l'élection\n");
for (int i = 0; i < listes.length; i++) {
System.out.println(listes[i]);
}
}
// seçim listelerinin karşılaştırma sınıfı
class CompareListesElectorales implements Comparator<ListeElectorale> {
// oy sayısına göre iki aday listesinin karşılaştırılması
@Override
public int compare(ListeElectorale listeElectorale1, ListeElectorale listeElectorale2) {
// bu iki listenin oyları karşılaştırılır
int nbVoix1 = listeElectorale1.getVoix();
int nbVoix2 = listeElectorale2.getVoix();
if (nbVoix1 < nbVoix2) {
return +1;
} else {
if (nbVoix1 > nbVoix2)
return -1;
else
return 0;
}
}
}
}
ElectionsConsole sınıfında çok az değişiklik vardır. Şu anda [métier] katmanındaki yöntemlerin ilk parametre olarak bir kullanıcı gerektirdiğini (satır 31, 49, 51) unutmamak gerekir. Bu kullanıcı, satır 21-22'deki enjeksiyon yoluyla sağlanır. Enjekte edilen kullanıcı, /jSON web sunucusuna sorgu göndermek için yetkilendirilmiş kullanıcıdır.
Yapılması gereken iş: Konsol uygulamasını test edin (önceden güvenli sunucuyu çalıştırmayı unutmayın).
17.6. [swing] katmanına sahip güvenli sunucunun istemcisi
![]() |
[elections-ui-metier-dao-webjson] projesini kopyalayıp yapıştırarak yeni bir NetBeans projesi olan [elections-swing-metier-dao-security-webjson]'i oluşturuyoruz:
![]() |
Yeni [2] projesinde:
- [elections.client.dao, elections.client.metier, elections.client.entities] paketlerini silin;
- [elections.client.ui] paketinde, [ElectionsConsole, IElectionsUI] sınıflarını silin;
- paketleri [2]'te belirtildiği gibi yeniden adlandırın;
- [elections.security.client.swing] paketinde, grafik arayüzünü uygulayan [AbstractElectionsSwing] sınıfını [AbstractElectionsMainForm] olarak ve[ElectionsMainForm] olarak yeniden adlandırın;
- [clic droit / Fix Imports] ile çeşitli sınıflardaki içe aktarma sorunlarını giderin;
Bu aşamada çok sayıda hata bulunmaktadır.
17.6.1. Maven Yapılandırması
[pom.xml] dosyası şu şekilde değişiyor:
<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>istia.st.elections</groupId>
<artifactId>elections-swing-metier-dao-security-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elections-swing-metier-dao-security-webjson</name>
<description>couche swing du client web / jSON</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>istia.st.elections</groupId>
<artifactId>elections-console-metier-dao-security-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 4-8. satırlar: yeni projenin Maven kimliği;
- 22-26. satırlar: 17.5. paragrafta incelediğimiz konsol projesine bir bağımlılık;
17.6.2. Spring Yapılandırması
Bu proje, içe aktardığı konsol projesinin Spring yapılandırmasını kullanır (bkz. 17.5.2. paragraf).
17.6.3. Swing uygulamasının görünümleri
![]() |
Bu projede iki görünüm kullanılacak:
![]() |
- [1] oturum açma görünümü yenidir. Bu görünüm, uygulamayı kullanmak isteyen kullanıcının kimliğini doğrulamak için kullanılır. Aşağıdaki sınıfları kullanır:
- [AbstractElectionsConnectForm] (görünümü uygulayan);
- [ElectionsConnectForm], görünümün olaylarını yönetir;
- [2] görünümü bilinmektedir. Bu, şimdiye kadar kullanılan görünümdür. Aşağıdaki sınıfları kullanır:
- [AbstractElectionsMainForm], görünümü uygular;
- [ElectionsMainForm], görünüm olaylarını yönetir;
17.6.4. Oturum
![]() |
Bir Swing uygulamasında birden fazla görünüm olduğunda, bir görünümün başka bir görünüme bilgi aktarabilmesi için bir yol bulunmalıdır. Web geliştirmede kullanılan bir kavramdan yararlanıyoruz: belirli bir kullanıcıyla ilişkili görünümlerin bilgi paylaşımına olanak tanıyan oturum. Bu kavram, burada her iki görünümün olaylarını yöneten iki sınıfa enjekte edilecek bir Spring singleton'u yardımıyla uygulanacaktır:
package elections.security.client.swing;
import elections.security.client.entities.User;
import org.springframework.stereotype.Component;
@Component
public class UiSession {
// oturum açmış kullanıcı
private User user;
// getter ve setter'lar
...
}
- 6. satır: [UiSession] sınıfı bir Spring bileşenidir;
- 10. satır: Bu sınıfın tek bir amacı vardır: [1] görünümüyle oturum açan kullanıcıyı kaydetmek. Bu kullanıcıyı bilmesi gereken [2] görünümü, kullanıcı bilgisini buradan alacaktır;
17.6.5. Boot sınıfı
![]() |
Grafik uygulamanın önyükleme sınıfı şöyledir:
package elections.security.client.boot;
import elections.security.client.console.IElectionsUI;
public class BootElectionsSwing extends AbstractBootElections {
public static void main(String[] arguments) {
new BootElectionsSwing().run();
}
@Override
protected IElectionsUI getUI() {
return ctx.getBean("electionsConnectForm", IElectionsUI.class);
}
}
- 5. satır: [BootElectionsSwing] sınıfı, projenin bağımlılıklarında bulunan konsol projesinde tanımlanan [AbstractBootElections] sınıfını genişletir;
- 10-13. satırlar: [BootElectionsSwing] sınıfı, [ElectionsConnectForm] oturum açma görünümünü görüntüleyecektir;
- 6-8. satırlar: Bu sınıfın [run] yöntemi çalıştırılacaktır;
17.6.6. [ElectionsMainForm] sınıfı
[ElectionsMainForm] sınıfı, daha önce [ElectionsSwing] olarak adlandırılan sınıftır. Bu sınıf, [AbstractElectionsMainForm] görünümünün olaylarını yönetir. Kodunda şu değişiklikler yapılmıştır:
package elections.security.client.swing;
import elections.security.client.console.IElectionsUI;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import elections.security.client.entities.ElectionsException;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import elections.security.client.metier.IElectionsMetier;
@Component
public class ElectionsMainForm extends AbstractElectionsMainForm implements IElectionsUI {
private static final long serialVersionUID = 1L;
// [métier] katmanına ilişkin referans
@Autowired
private IElectionsMetier metier;
// oturum UI
@Autowired
private UiSession uiSession;
// oturum açmış kullanıcı
private User user;
// liste şablonları JList
private DefaultListModel<String> modèleNomsVoix = null;
private DefaultListModel<String> modèleRésultats = null;
// yarışmadaki listeler
private ListeElectorale[] listes;
// kullanıcı tarafından girilen listeler
private final List<ListeElectorale> listesSaisies = new ArrayList<>();
private ListeElectorale[] tListesSaisies;
// başlatma işlemleri
@Override
protected void init() {
// üst sınıf tarafından bileşenlerin oluşturulması
super.init();
// yerel başlatmalar
modèleNomsVoix = new DefaultListModel<>();
jListNomsVoix.setModel(modèleNomsVoix);
modèleRésultats = new DefaultListModel<>();
jListResultats.setModel(modèleRésultats);
String info;
boolean erreur = false;
try {
// [métier] katmanından listeler istenir
listes = metier.getListesElectorales(user);
// liste adları jComboBoxNomsListes açılır menüsüne eşleştiriliyor
for (int i = 0; i < listes.length; i++) {
jComboBoxNomsListes.addItem(String.format("%s - %s", listes[i].getId(), listes[i].getNom()));
}
// ve seçim parametreleri
int nbSiegesAPourvoir = metier.getNbSiegesAPourvoir(user);
double seuilElectoral = metier.getSeuilElectoral(user);
// bu iki bilgiyle ilişkili etiketler başlatılır
jLabelSAP.setText(jLabelSAP.getText() + nbSiegesAPourvoir);
jLabelSE.setText(jLabelSE.getText() + seuilElectoral);
// başarı mesajı
info = "Source de données lue avec succès";
} catch (ElectionsException ex1) {
// hata kaydedilir
info = getInfoForException("Les erreurs suivantes se sont produites :", ex1);
erreur = true;
} catch (RuntimeException ex2) {
// hata kaydedilir
info = getInfoForException("Les erreurs suivantes se sont produites :", ex2);
erreur = true;
}
// hata mı?
if (erreur) {
// bilgi görüntüleniyor
jTextPaneMessages.setText(info);
jTextPaneMessages.setCaretPosition(0);
return;
} else {
jTextPaneMessages.setText("");
}
// form durumu
Utilitaires.setEnabled(new JLabel[]{jLabelAjouter, jLabelCalculer, jLabelEnregistrer, jLabelSupprimer}, false);
Utilitaires.setEnabled(
new JMenuItem[]{jMenuItemAjouter, jMenuItemCalculer, jMenuItemEnregistrer, jMenuItemSupprimer}, false);
// pencereyi ortala
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
}
...
@Override
public void run() {
// kullanıcı bilgilerini kaydet
user = uiSession.getUser();
// pencere başlatma
init();
setVisible(true);
}
}
- 32-33. satırlar: uygulama oturumunun eklenmesi;
- 112-119. satırlar: görünüm, daha önce olduğu gibi [run] yöntemi ile etkinleştirilecektir;
- 115. satır: oturumdan kullanıcı alınır. Bu kullanıcı, [ElectionsConnectForm] oturum açma görünümünün kodu tarafından buraya eklenmiştir. Ardından, [métier] katmanına yapılan çağrıların ilk parametresinin bu kullanıcı olması için kod değiştirilir (örneğin, satır 63, 69-70);
17.6.7. [AbstractElectionsConnectForm] görünümü
![]() |
NetBeans ile aşağıdaki formu oluşturun:
![]() |
Sınıf, [Connexion] menü seçeneğine tıklamayı yöneten yöntemi kendisi uygulamayacaktır. Sınıf, soyut olarak tanımlanmış [doConnecter] yöntemini çağıracak ve görünüm sınıfı da kendisi soyut olarak tanımlanacaktır. Otomatik olarak oluşturulan [main] statik yöntemi silinecektir.
17.6.8. Giriş görünümünün olay yönetimi
![]() |
[ElectionsConnectForm] sınıfı, oturum açma görünümünün olay yönetimini şu şekilde uygular:
package elections.security.client.swing;
import elections.security.client.console.IElectionsUI;
import elections.security.client.entities.ElectionsException;
import elections.security.client.entities.User;
import elections.security.client.metier.IElectionsMetier;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.SwingUtilities;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ElectionsConnectForm extends AbstractElectionsConnectForm implements IElectionsUI {
private static final long serialVersionUID = 1L;
// katman referansı [métier]
@Autowired
private IElectionsMetier metier;
// oturum açmış kullanıcı
private User user;
// ana form
@Autowired
private ElectionsMainForm electionsMainForm;
// oturum UI
@Autowired
private UiSession uiSession;
@Override
protected void doConnect() {
String info = null;
try {
if (isPageValid()) {
// kullanıcı kimlik doğrulaması
metier.authenticate(user);
// kullanıcı oturumda saklanır
uiSession.setUser(user);
// giriş ekranı gizlenir
setVisible(false);
// ana görünüm görüntülenir
electionsMainForm.run();
}
} catch (ElectionsException ex1) {
// hata kaydedilir
info = getInfoForException("Les erreurs suivantes se sont produites :", ex1);
} catch (RuntimeException ex2) {
// hata kaydedilir
info = getInfoForException("Les erreurs suivantes se sont produites :", ex2);
}
// hata mı?
if (info != null) {
// bilgi görüntüleniyor
jTextPaneErreurs.setText(info);
jTextPaneErreurs.setCaretPosition(0);
}
}
// başlatma işlemleri
@Override
protected void init() {
// üst sınıf tarafından bileşenlerin oluşturulması
super.init();
// yerel başlatmalar
...
// pencereyi ortala
...
}
@Override
public void run() {
// grafik kullanıcı arayüzü görüntüleniyor
SwingUtilities.invokeLater(new Runnable() {
public void run() {
init();
setVisible(true);
}
});
}
private boolean isPageValid() {
...
}
private String getInfoForException(String message, ElectionsException ex) {
...
}
private String getInfoForException(String message, RuntimeException ex) {
...
}
}
- 73-82. satırlar: [BootElectionsSwing] önyükleme sınıfı tarafından çağrılacak olan [run] yöntemi;
- 26-27. satırlar: kullanıcı tanındığında görüntülenmesi gereken 2 numaralı görünüme bir referans eklenmesi;
- satır 30-31: Uygulama oturumunun eklenmesi;
- 84. satır: [isPageValid] yöntemi iki şey yapar:
- kimlik bilgisinin boş olmadığını kontrol eder (şifre boş olabilir);
- 23. satırdaki User alanını girilen verilerle doldurur;
![]() | ![]() |
![]() | ![]() |
Yapılacak iş: Sınıf kodunu tamamlayın.
Yapılacak iş: Uygulamayı test edin.































