| Version 9 (modified by , 3 days ago) ( diff ) |
|---|
Други теми (Перформанси, безбедност)
Безбедност
- Апликацијата користи Spring Security што е докажан стандард за сетирање на безбедност во апликациите.
- Се користи дозволување на клиент кој може да праќа барања до backend
config.setAllowedOrigins(List.of("http://localhost:5173")); - Само рутата за логирање на корисници е дозволена за целата јавност, другите рути бараат автентикација
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/login").permitAll()
.anyRequest().authenticated()
)
- Сите корисници се креирани од директор админ и нема стандардна регистрација.
Хеширање на лозинки (Заштита на податоци)
Апликацијата користи BCryptPasswordEncoder како начин за хеширање на лозинки.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Кој потоа се користи при хеширање:
String encodedPass = passwordEncoder.encode( blagajnikRequestDTO.getLozinka());
Авторизација (Role-Based Access Control)
Апликацијата користи Roles за дозвола на пристап на некоја активност. Исто така се користи и method security т.е. се користи @PreAuthorize анотацијата за заштита на рутите:
@PreAuthorize("hasRole('DIREKTOR_ADMIN')")
@GetMapping
public ResponseEntity<List<KlasResponseDTO>> getAllKlasovi(){
List<KlasResponseDTO> klasovi = this.klasService.getAllKlasovi();
return new ResponseEntity<>(klasovi, HttpStatus.OK);
}
@GetMapping("/{classId}/students")
@PreAuthorize("hasAnyRole('KLASEN_RAKOVODITEL','DIREKTOR_ADMIN', 'PREDMETEN_NASTAVNIK', 'RODITEL')")
public ResponseEntity<List<UcenikResponseDTO>> getAllStudentsWhoAreInTheSameClass(@PathVariable UUID classId){
List<UcenikResponseDTO> getAllStudentsWhoAreInTheSameClass = this.klasService.getAllStudentsWhoAreInTheSameClass(classId);
return new ResponseEntity<>(getAllStudentsWhoAreInTheSameClass, HttpStatus.OK);
}
Користење на сесии
Апликацијата користи сесии и сесиско колаче за автентикација на сесијата на корисникот.
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
);
Но, и автоматско бришење на сесиското колаче при одјава:
.logout(logout->logout
.logoutUrl("/api/auth/logout")
.deleteCookies("JSESSIONID")
.logoutSuccessHandler((request, response, authentication) -> {
response.setStatus(200);
})
Спречување на SQL Injection
За извршување на SQL прашањата, користиме binding. Односно Spring Boot ги третира овие параметри како податоци а не како извршен код. Дел од кодот:
@Modifying
@Transactional
@Query(nativeQuery = true, value = "INSERT INTO Usna_Ocenka (id, tema, osvoeni_poeni, max_poeni, vid_isprasuvanje) VALUES( :id,:tema, :osvoeni_poeni, :max_poeni, :vid_isprasuvanje)")
void insertUsnaOcenka(
@Param("id") UUID id,
@Param("tema") String tema,
@Param("osvoeni_poeni") Integer osvoeniPoeni,
@Param("max_poeni") Integer maxPoeni,
@Param("vid_isprasuvanje") String vidUsnoIsprasuvanje
);
Безбеднст на ниво на база
Имаме имплементирано посебен корисник кој што не е главниот user:
Исто така, користиме тип enum на ниво на база дополнително да се осигураме дека станува збор за вистинската категорија.
CREATE TYPE payment_status AS ENUM (
'KREIRANO',
'TX_SUBMITTED',
'ODOBRENO',
'ZAVRSENO',
'FAILED'
);
CREATE TYPE notice_status AS ENUM (
'PENDING',
'APPROVED',
'REJECTED'
);
CREATE TABLE Plakjanje (
id UUID PRIMARY KEY DEFAULT gen_random_uuid (),
status payment_status NOT NULL,
tx_hash VARCHAR(100) UNIQUE,
valuta VARCHAR(10) NOT NULL,
plateno_Na TIMESTAMP,
iznos DECIMAL(38, 18) NOT NULL,
platenoOdRoditel_Id UUID NOT NULL,
soopstenie_za_plakjanje_id UUID NOT NULL,
created_at TIMESTAMP DEFAULT now(),
UNIQUE (
platenoOdRoditel_Id,
soopstenie_za_plakjanje_id
),
CONSTRAINT chk_tx_hash_when_paid CHECK (
(
status = 'KREIRANO'
AND tx_hash IS NULL
)
OR (
status IN (
'TX_SUBMITTED',
'ODOBRENO',
'ZAVRSENO',
'FAILED'
)
AND tx_hash IS NOT NULL
)
),
FOREIGN KEY (soopstenie_za_plakjanje_id) REFERENCES SoopstenieZa_Plakjanje (id),
FOREIGN KEY (platenoOdRoditel_Id) REFERENCES Roditel (id)
);
Користиме и pgcrypto модулот за генерирање на UUID, што дополнително ја зголемува безбедноста за погодување на id и влечење на информации.
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; id UUID PRIMARY KEY DEFAULT gen_random_uuid (),
Перформанси
Сценарио 1 - Преглед на присуство за ученик - полугодишно
Во распоредот за часови, може да имаме 7 часа дневно, 5 дена во неделата. Тоа се 35 часа неделно или 140 часа месечно. Во превод, за еден месец имаме 140 записи за присуство на месечно ниво.
Присуството може да се филтрира по месеци поради тоа што за едно полугодие имаме 560 записи за присуство и тоа само за еден ученик. Но, доколку класниот раководител или родителот сака да го види присуството за едно полугодие за ученикот, тогаш мора да се пребара низ огромна табела.
SQL Query за прво полугодие:
explain ANALYZE SELECT p.id AS p_id, p.datum, p.status, p.zabeleska, k.id AS n_id, k.e_posta, k.ime, k.prezime, k.pol, k.adresa, k.kreiranod_id, k.active, c.id AS c_id, c.ime, c.reden_cas, c.predmet_id, c.active, c.denvo_nedelata FROM prisustvo p JOIN nastavnik n ON p.evidentiranoOd_Id = n.id JOIN korisnik k ON n.id = k.id JOIN cas c ON p.zaCasot_Id = c.id WHERE p.seOdnesuvaNaUcenikot_Id ='18d82d24-9d9f-4c0b-9692-f7a87174cb4c' AND p.datum BETWEEN '2026-09-01' AND '2026-12-31' ORDER BY datum DESC
Анализирање:
