%---------------------------------------------------------------------------- \chapter{Szerver oldal} \label{chapt:birdmap-backend} %---------------------------------------------------------------------------- Ebben a fejezetben bemutatom a szerveroldal architektúráját, felépítését. Ismertetem a különböző szoftver komponensek feladatát. %---------------------------------------------------------------------------- \section{Architektúra} %---------------------------------------------------------------------------- A szerveroldal fejlesztésénél a háromrétegú architektúrát alkalmaztam, melynek lényege, hogy az alkalmazást logikailag három elkülönülő részre bontjuk: \begin{itemize} \item \textbf{Adat elérési réteg}. Ez a rész felel a tárolt entitások modell definícióiért, illetve azoknak a kiolvasásáért, tárolásáért egy adatbázisból vagy fájlrendszerből. \item \textbf{Megjelenítési réteg}. Ezen réteg feladata a kliensoldal közvetlek kiszolgálása. Bármilyen irányú kommunikáció a kliensek felé ezen a rétegen keresztül történik. \item \textbf{Üzleti logikai réteg}. Minden ami nem a közvetlen kommunikációért, megjelenítésért vagy adat elérésért, tárolásért felel, az ide kerül. A fenti két réteg között helyezkedik el és feladata a különböző folyamatok értékelése és futtatása, valamint az adatok feldolgozása. \end{itemize} Az ASP.NET Core beépítetten támogatja a dependency injection-t, mely a \verb+Startup+ osztály \verb+ConfigureServices+ metódusával konfigurálható. Én minden rétegbe tettem egy ilyen \verb+Startup+ osztályt, hogy azok feleljenek a saját szolgáltatásaik konfigurálásáért és regisztrálásáért. %---------------------------------------------------------------------------- \section{Adat elérési réteg} %---------------------------------------------------------------------------- Az adatelérést az Entity Framework Core segítségével oldottam meg. Telepítettem egy MSSQL adatbázis szervert a számítógépemre, melynek csatlakozási paramétereivel a \verb+Startup+ osztályban felkonfigurálom az EF Core által nyújtott \verb+DbContext+ saját leszármazott változatát. Így csak az entitások elkészítése és azok alapértelmezett értékeinek az adatbázisba való feltöltése marad hátra. %---------------------------------------------------------------------------- \subsection{Entitások} %---------------------------------------------------------------------------- Mivel az adatok nagy részét külső szolgáltatások fogják nyújtani, így lokálisan összesen két entitás létrehozására volt szükség. Az egyik a \verb+User+, mely az alkalmazás felhasználóinak adatait tárolja. A másik a \verb+Service+, mely a külső szolgáltatások adatainak tárolását szolgálja, amelyeket azért tárolok az adatbázisban és nem mondjuk a konfigurációs fájlban, mert szerettem volna, hogyha a kezelőfelületen lehetne őket szerkeszteni, törölni. \lstset{style=sharpc, morekeywords={record, get, set}} \begin{lstlisting}[caption=A User és a Service modell] public record User { public int Id { get; set; } public string Name { get; set; } public byte[] PasswordHash { get; set; } public byte[] PasswordSalt { get; set; } public Roles Role { get; set; } public bool IsFromConfig { get; set; } } public record Service { public int Id { get; set; } public string Name { get; set; } public Uri Uri { get; set; } public bool IsFromConfig { get; set; } } \end{lstlisting} Az alkalmazás használata szempontjából a felhasználók két csoportba oszlanak. Vannak adminisztrátor és sima felhasználók, utóbbi csak az adatok olvasására, míg előbb azok módosítására is jogosult. A \verb+Role+ mező ennek a megkülönböztetsnek a jelzője. %---------------------------------------------------------------------------- \subsection{Seedelés} %---------------------------------------------------------------------------- Az alkalmazás konfigurációs fájljából meg lehet adni alapértelmezett felhasználókat és szolgáltatásokat. Ezeknek megkülönböztetésére szolgál az entitások \verb+IsFromConfig+ mezője. A szerver indítása legelején, megvizsgálja, hogy létezik-e az adatbázis és ha igen kitöröl minden olyan entitást ahol az \verb+IsFromConfig+ mező igaz. Majd hozzáadja az újonnan beolvasott értékeket. %---------------------------------------------------------------------------- \section{Üzleti logikai réteg} %---------------------------------------------------------------------------- Ebben a rétegben található meg a szerver legtöbb szolgáltatása. It vannak implementálva a Birdnetes Command and Control és Input komponensekkel kommunikáló szolgáltatások is, melyeket azok OpenAPI leírói alapján az NSwag\cite{nswag} alkalmazással generáltam. Az OpenAPI a klienseken kívül definiálja még az azok által használt modelleket is. A Command and Control által használt \verb+Device+ modell tartalmazza annak egyedi azonosítóját, státuszát, koordinátáit és a használt szenzorok listáját, melyeknek szintén van egy modellje \verb+Sensor+ néven. Ennek szintén van azonosítója és státusza. Az Input szolgáltatásnak is van saját modellje, amely a hangüzenetek metaadatait reprezentálja. Többek között tartalmazza a kihelyezett eszköz egyedi azonosítóját és a hangüzenet keltének dátumát. Ugyan itt található meg a \verb+User+ és \verb+Service+ entitások létrehozásáért, olvasásáért, szerkesztéséért és törléséért felelős szolgáltatások is. Valamint itt található még az autentikációért felelős szolgáltatás is. A felhasználók jelszavainak tárolására a HMAC (Hash-based Message Authentication Code) algorithmust, pontosabban annak a \verb+HMACSHA512+\cite{hmacsha512} C\# implementációját használtam. Minden jelszóhoz generálok egy egyedi kulcsot és azzal egy hash-t, majd ezeket tárolom a \verb+User+ modell \verb+PasswordSalt+ és \verb+PasswordHash+ mezőiben. Amikor egy felhasználó be akar jelentkezni először megvizsgálom, hogy egyáltalán létezik-e az adatbázisban az adott nevű felhasználó, ha igen, akkor a megadott jelszóból az imént említett folyamattal generált kulcsot és hash-t összehasonlítom az adatbázisban tárolttal. %---------------------------------------------------------------------------- \section{Megjelenítési réteg} %---------------------------------------------------------------------------- A fejezet elején említett \verb+Startup+ osztály ebben a rétegben található, itt kerülnek az egyes szolgáltatások regisztrálásra. Többek között a naplózás is itt kerül inicializálásra, mely az NLog saját konfigurációs fájljával történik. Meg lehet adni különböző szűrőket és kimeneteket, amellyel szelektálni lehet, hogy az egyes naplózott események hova kerüljenek. Például az MQTT szolgáltalás napló bejegyzéseit a \ref{lst:nlog-config} lista alapján szűrtem. Minden \verb+Debug+ szintől nagyobb és \verb+Error+ szinttől kisebb bejegyzés, mely tartalmazza az \verb+Mqtt+ kulcsszót az \verb+mqttFile+ azonosítójú fájlba kerül. \lstset{style=xml, morekeywords={targets, target, xsi:type, name, fileName, layout, rules, logger, name, minlevel, maxlevel, writeTo, final}} \begin{lstlisting}[caption=Az NLog.config fájl egy részlete, label=lst:nlog-config] ... ... ... ... \end{lstlisting} % program.cs startup.cs middlewares logolás seedelés swagger %---------------------------------------------------------------------------- \subsection{Kommunikációs Szolgáltatások} %---------------------------------------------------------------------------- mqtt signalR %---------------------------------------------------------------------------- \subsection{Kontrollerek} %---------------------------------------------------------------------------- Controllersw Dtos mapper