Kihagyás

Alkalmazássablonok

Ahogyan minden program, úgy a webalkalmazások is valamilyen struktúra/szerkezet szerint épülnek fel. Ilyen struktúra a 3 fő komponensből álló modell-view-controller (MVC) architekturális model is. A minta alkalmazásával az üzleti logika elkülöníthető a felhasználói felülettől.

mvc

View komponens: az információ reprezentálása, esetünkben ez a felhasználói felület lesz (de lehet bármilyen más vizualizáció: ábra, táblázat)

Controller komponens: a felhasználói interakciókra reagálva utasításokat hajt végre a model komponensen

Model komponens: az alkalmazás által használt adat(ok) menedzselése, leképezése dinamikus struktúrába

Ezen kívül érdemes megemlíteni a DAO (Data Access Object) adatelérési réteget, mely a konkrét adatelérésért, módosításért, törlésért felelős (legyen az egy adatbázisban vagy egy fájlban).

A webalkalmazások tipikusan kliens-szerver architektúra segítségével működnek, azaz a kliens valamilyen kérést intéz a szerver felé (request), amely feldolgozza a kérést, szükség esetén kapcsolatba lép az adatábzissal, majd válaszol a kliensnek (response), amely általában valamilyen HTML vagy JSON formátumú adat. Ezek a kérések tipikusan a HTTP protokollt használják és a kérések célja valamilyen erőforrás elérése (más terminológiában ezeket az erőforrásokat endpoint-oknak, végpontoknak is szokták nevezni). Esetünkben az erőforrások címzése URL-n keresztül történik:

path

A 4 HTTP kérés megfeleltethető a CRUD műveleteknek:

Jelenleg a HTTP standard csupán a GET és a POST kéréseket támogatja HTML form-ok keretében. A tárgy keretein túlmutató megoldásokkal megoldható lenne másfajta kérések indítása is (pl. különböző webes keretrendszerek alkalmazásával), viszont a mintaalkalmazások csak GET és POST kérésekkel operál.

A minta alkalmazások forráskódjai a következő címen érhetőek el

Spring Boot (Java): https://git-okt.sed.inf.szte.hu/markusa/rf1-pelda/tree/java
Node.js (JavaScript): https://git-okt.sed.inf.szte.hu/markusa/rf1-pelda/tree/node

A kiadott alkalmazások nem teljesek

A gyorsabb átláthatóság érdekében a programok felépítése le lett egyszerűsítve, illetve bizonyos esetekben hiányzik az input adatok validálása vagy éppen a hibakezelés. A cél, hogy a hallgató átlássa a webalkalmazás felépítését: hogyan történik az adatbázisból az adat kiolvasása, az milyen módon kerül átadásra a felhasználói felületnek és ott hogyan kerül megjelenítésre. Ezt követően a sablon a választott témának megfelelően könnyebben testreszabható lesz.

Lokális PostgeSQL adatbázis létrehozása

A mintaalkalmazások egy PostgreSQL (https://www.postgresql.org/) adatbázishoz fognak csatlakozni.

Első lépésben telepítsük az operációs rendszerünknek megfelelő PostgreSQL szervert az itt leírtak alapján (érdemes a legfrissebb verzióval dolgozni, és mindent az alapértelmezett beállításokon hagyni - és a megadott jelszót ne felejtsük el):

Adatok létrehozása az adatbázisban

Hogy az adatbázisban tárolt adatokra épülő további feladatok is zökkenőmentesen haladjanak, célszerű az adatbázis létrehozásához szükséges utasításokat és az adatokakat egy .sql kiterjesztésű állományba elmenteni és a GitLab-on tárolni.

Sikeres telepítést követően akár csatlakozzunk a lokális adatbázishoz psql segítségével és másoljuk bele az alábbi utasításokat, amely létrehozza a dogs és a users táblát, amit később az alkalmazásunk használni tud majd.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
DROP TABLE IF EXISTS users;

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(100) NOT NULL,
    password VARCHAR(100) NOT NULL,
    role VARCHAR(100) NOT NULL
);

DROP TABLE IF EXISTS dogs;

CREATE TABLE dogs (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    age INT NOT NULL,
    owner_id INT REFERENCES users(id)
);

Amennyiban sikeresen lefutottak az utasítások, az alkalmazásunk már használni tudja az - egyelőre - üres táblákat:

mvc

Java

Spring Boot egy nyílt forráskódú Java alkalmazás keretrendszer, amellyel webalkalmazásokat is készíthetünk. Ebben a példában egy kutyákat nyilvántartó rendszert mutatunk be, amely képes az adatbázisba adatot menteni, kiolvasni, módosítani és törölni. Ezen kívül eltároljuk a kutya tulajdonosát is, ezért a példa tartalmazza a felhasználókezeléshez szükséges műveleteket is. Az adatok dinamikus megjelenítését a Thymeleaf (https://www.thymeleaf.org/) fogja elvégezni, ami egy szerver oldali ún. template engine. Ezt nem kell külön telepítenünk, a mintaalkalmazás már tartalmazza, és a fő feladata, hogy az adatbázisban tárolt adatok alapján az előre elkészített sablonokból (template-ekből) HTML kódot állítson elő, amit aztán a kliens böngészője megjelenít (a kliens oldali "generáláshoz" pl. Angular vagy React keretrendszer használható).

Szükséges eszközök

Első lépésként szükségünk lesz egy IDE-re, mi Eclipse-t vagy IntelliJ-t ajánlunk:

Illetve szükségünk lesz egy JDK-ra is (a mintaalkalmazás Java 8-at használ): https://www.oracle.com/java/technologies/javase-downloads.html

Spring Boot alkalmazás esetén szükséges valamilyen projektmenedzsment eszközt használni, amellyel többek között a projekt build-elést tudjuk automatizálni. Egy nagyobb szoftver projekt temérdek programcsomagot tartalmazhat, melyeket összefoglaló néven függőségeknek (dependency) szoktunk nevezni (pl. saját email küldő szolgáltatás megírása helyett használhatunk valaki más által megírt, nyilvánosan elérhető kódot, azaz függőséget).

Egy ilyen projektmenedzsment eszköz a Maven is, amely tehát képes a külső könyvtárakat (pl. adatbáziskezelő könyvtárakat) kezelni. Alapesetben ezért a Maven (https://maven.apache.org/download.cgi) telepítése is szükséges lenne, viszont az Eclipse és az IntelliJ már támogatja/tartalmazza ezt a szoftvert. A Maven alapja a pom.xml fájl, amelyben ezek a függőségek illetve a build folyamat van definiálva, illetve tipikusan ennek a fájlnak a helyét kell megadni, ha importálni akarjuk a projektünket a megfelelő IDE-be. További információ: https://en.wikipedia.org/wiki/Apache_Maven

Eclipse-ben a következő módon tudunk Maven projektet megnyitni: File -> Import -> Maven -> Existing Maven Projects, majd adjuk meg a Browse gombra kattintva a pom.xml elérési útvonalát és nyomjuk meg a Finish gombot. Ezt követően az Eclipse automatikusan importálja a függőségeket.

IntelliJ esetében elegendő a projektet megnyitni, ezt követően automatikusan felismeri, hogy Maven projektről van szó és importálja a függőségeket.

A program felépítése, futtatása

A Spring Boot alkalmazás belépési pontja az ​ Application.java​ osztályban van. Hierarchikusan ennek kell a legmagasabb szinten lenni. Spring Boot esetén fontos jelentősége van az annotációknak a kódban, a legfontosabbak listázzuk ki most:

  • @GetMapping: Az adott HTTP GET kéréshez rendeli a megfelelő metódust

  • @PostMapping: Az adott HTTP POST kéréshez rendeli a megfelelő metódust

  • @RequestParam: Az ún. query paraméter lekérésre szolgál, tipikiusan egy HTML form paramétereit tudjuk így elérni

  • @PathVariable: az adott URI-ben található változót tudjuk elérni vele, pl. /dog/1, /dog/2 erőforrások esetén a path variable az 1-es vagy a 2-es érték lesz

  • @Autowired: egy függőség deklarálását teszi lehetővé, pl. az adott HTTP kéréshez rendelt metódusban használni tudunk adatbáziskapcsolathoz szükséges hívásokat

  • @Repository: adatbázis jellegű tulajdonsággal tudunk felruházni egy osztályt

  • @Controller: egy osztályt felruházunk olyan tulajdonságokkal, amellyel képes lesz HTTP kéréseket kiszolgálni és a megfelelő nézeteket meghívni

A példánkban a ​ DogController.java és a UserController.java​ fájlok tartalmazzák a REST végpontokat, azaz azokat az útvonalakat ahol a GET és POST műveletek meg vannak valósítva. Nézzünk 1-1 példát a GET és a POST végpontokra, amelyek lekezelik a webalkalmazást használó felhasználó navigálásait. Amennyiben a domain nevünk (pl. valami.com vagy localhost:8080) után egy / jelet teszünk, akkor az ehhez a végponthoz tartozó metódus kerül végrehajtásra (egyébként ez az alapértelmett, tehát ha nem írjuk ki, akkor is a / útvonal töltődik be).

Ebben a metódusban meghívjuk a dogDAO.listDogs() metódust, amely tartalmazza a tényleges SQL utasítást az adatbázisban található összes kutya lekéréséhez. Mivel a dogs táblában a tulajdonosának csak az azonosítója (owner_id) szerepel, ezért egy másik listában összegyűjtjük a felhasználók email címét is. A célunk az lesz, hogy alapesetben töltsük be és jelenítsük meg táblázatos formában az összes kutyát és az adatait. A két listát és az aktuális bejelentkezett felhasználót reprezentáló objektumot (ha létezik, ellenkező esetben egy "üres" User objektumot) átadunk a view-nak (jelen esetben a Thymeleaf utasításokkal kiegészített index.html fájlnak). Ehhez a Model objektumra van szükség, amely azokat az adatokat tartalmazza, amelyeket a webalkalmazás a nézet (view) rétegnek továbbít, hogy azokat megjelenítse a felhasználó számára.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 @GetMapping(value = "/")
  public String listDog(Model model) {
    List < String > user_mails = new ArrayList < String > ();
    List < Dog > dogList = dogDAO.listDogs(); 

    model.addAttribute("dogs", dogList); 

    for (Dog dog: dogList) {
      user_mails.add(userDAO.getUserById(dog.getOwner_id()).getEmail());
    }

    model.addAttribute("user_mails", user_mails);

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication.getName().equals("anonymousUser")) {
      // ha nincs senki bejelentkezve, akkor egy üres objektumot adunk vissza
      model.addAttribute("current_user", new User()); 
    } else {
      // ha van bejelentkezett felhasználó, akkor lekerjük az adatbázisból az adatait
      model.addAttribute("current_user", userDAO.getUserByEmail(authentication.getName())); 
    }

    return "index";
  }

Amennyiben a domain nevünk után írjuk a /add útvonalat, akkor az ehhez az útvonalhoz rendelt metódus kerül végrehajtásra. Két érték érkezett egy HTML form-ból, name és age, amely egy új kutyát reprezentál. Ezenkívül szükség lesz még az aktuális felhasználó azonosítójára, ezért lekérjük az aktuális felhasználót az userDAO.getUserByEmail(..) metódus segítségével. Az adatbázisba történő mentéshez meghívjuk az dogDAO.insertDog(..) metódust, amely elmenti az adatbázisba az új kutyát, majd átirányítjuk az oldalt a /-hez tartozó nézetre.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@PostMapping(value = "/add")
  public String addDog(@RequestParam("name") String name, @RequestParam("age") int age) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String currentPrincipalName = authentication.getName();
    User user = userDAO.getUserByEmail(currentPrincipalName);
    Dog dog = new Dog(name, age, user.getId());
    dogDAO.insertDog(dog);

    return "redirect:/";
  }

A ​Dog.java lényegében az adatbázistábláinkról készített model, mindig egy ilyen modelbe konvertáljuk a lekérdezések eredményét. A DogDAO.java tartalmazza a tényleges SQL utasításokat, amelyeket a Controller osztály metódusai hívják meg.

A 4 alapművelet a következő (CRUD): Create, Read, Update, Delete. Ezt követik a Controller-ek és a DAO osztályok is.

A lényeges része még az alkalmazásnak a statikus fájlok (pl. css), illetve az adatbázishoz való kapcsolódás adatai is, ezeket a fájlok a​ resource ​ mappában találhatóak. Kiemelendő az application.properties fájl, ahol az adatbázis azonosítása történik (elérhetőség, felhasználónév, jelszó):

1
2
3
spring.datasource.url = jdbc:postgresql://HOST:PORT/DATABASE
spring.datasource.username=USER
spring.datasource.password=PASSWORD

Végezetül a ​templates mappa tartalmazza a HTML fájlokat az adatok megjelenítéséhez. Említettük korábban az ún. index nézetet, ez az index.html fájlt jelenti. Itt a Thymeleaf konstrukcióit felhasználva fogjuk tudni megjeleníteni az adatokat.

Például a kutyák listázásához használt, a DogController osztályban a listDog(Model model) metódus paraméterében lévő model-hez rendeljük hozzá a lekérdezés során kapott eredményeket dogs változóként (model.addAttribute(..) függvénnyel). Ez tartalmazza az összes kutyát, amelyet aztán képesek vagyunk iterálni a megjelenítéshez táblázatos formában:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<tr th:each="dog, iter : ${dogs}">
  <td th:text="${dog.id}"></td>
  <td th:text="${dog.name}"></td>
  <td th:text="${dog.age}"></td>
  <td th:text="${user_mails[iter.index]}"></td>
  <td th:if="${user_mails[iter.index]} == ${current_user.email} or ${current_user.role} == 'ROLE_ADMIN'"><a th:href="@{/edit/{id}(id=${dog.id})}">Update</a></td>
  <td sec:authorize="hasAuthority('ROLE_ADMIN')"><button data-toggle="modal" data-target="#modal-toTrash"
    th:attr="data-target='#modal-toTrash'+${dog.id}" class="btn-sm btn-dark details">Delete
    <span class="glyphicon glyphicon-trash" data-toggle="tooltip"
    data-placement="top" title="Sent to Trash Box"></span></button>
  /td>

   ...
</tr>

Ebben a HTML form-ban új kutyát tudunk hozzáadni és a megfelelő REST végpontot meghívni. A request POST hívás lesz:

1
2
3
4
5
6
7
8
9
<form action="#" th:action="@{/add}" method="post">
  <label>Name:
    <input type="text" name="name" id="name"></input>
  </label>
  <label>Age:
    <input type="text" name="age" id="age"></input>
  </label>
  <input type="submit" value="Submit" class="btn-sm btn-dark details"></input>
</form>

A lokális szerver elindításához futtassuk az Application.java fájlt az IDE-ben:

java-eclipse

Amennyiben minden lépésünk sikeres, úgy a​ http://localhost:8080/​ címen fog futni a programunk.

dog-eclipse

Felhasználókezelés

Ebben a példában a users táblát nem csak a kutya tulajdonosának meghatározásához használjuk, hanem bemutatjuk, hogy hogyan tudunk felhasználói jogosultságok (a példánkban ROLE_USER és ROLE_ADMIN) alapján a kölönböző útvonalakat (végpontokat) levédeni.

A felhasználó kezelés esetén 2 alapfogalmat érdemes tisztázni:

Authentication: a folyamat, amely igazolja, hogy a felhasználó valóban az, aki (tipikusan jelszóval azonosítja magát)

Authorization: a folyamat, amely igazolja, hogy a felhasználónak van-e megfelelő jogosultsága az adott erőforrás (pl. egy adott nézet) megtekintéséhez

Spring Boot esetén a fejlesztőről sok terhet levesz a Spring Security (https://spring.io/projects/spring-security), talán a leglátványosabb az automatikusan definiált login.html fájl, amit nem szükséges külön elkészíteni (és mégis hozzárendelhető a UserController.java osztályban a /login útvonalhoz).

A felhasználó objektumot a User.java model tartalmazza. Ahhoz, hogy a Spring Security használni tudja az autentikációhoz, implementálnia kell a UserDetails interfészt és a szükséges metódusokat, például jogosultságot visszaadó getAuthorities() metódust.

A service package-ben szerepel még a UserDetailsService interfészt implementáló osztály, amelyet a Spring Security az aktuális felhasználó adatainak betöltéséhez használja. Azaz ezt az interfészt használja a felhasználónév (esetünkben e-mail cím), a jelszó és a jogosultságok ellenőrzéséhez.

Végezetül a config package-ben van a WebSecurityConfig fájl, amely segtségével ellenőrizni tudjuk a HTTP kéréseket, azaz itt tudjuk megadni, hogy az adott útvonalhoz milyen jogosultsági szint kell. Ahogy látszódik, a /edit/.. útvonal eléréshez csak admin vagy user jogosultsággal rendelkező felhasználó férhet hozzá. Ezt ki is próbálhatjuk, ha inkognitó módban megpróbáljuk autentikáció nélkül szerkeztésre megnyitni egy kutyát (pl. http://localhost:8080/edit/10).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .antMatchers("/edit/*").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
      .anyRequest().permitAll()
      .and()
      .formLogin()
      .defaultSuccessUrl("/")
      .permitAll()
      .and()
      .logout().logoutSuccessUrl("/").permitAll();
  }
Továbbá érdemes a HTML fájlokat részletesen áttekinteni, hogyan tudunk a bejelentkezett felhasználó jogosultságától függően HTML tag-eket elrejteni a view létrehozása során a Thymeleaf engine-hez tartozó utasításokkal.

Node.js

A második sablonalkalmazásunk szintén a kutyanyilvántartó rendszert valósítja meg. Most a Node.js-re épülő Express keretrendszert használjuk, a dinamikus tartalom megjelenítéséhez pedig EJS-t: https://ejs.co/

A fejlesztéshez most is Debian alapú operációs rendszert használunk, de természetesen itt is igaz, hogy más OS is használható.

Szükséges eszközök

Itt is szükségünk lesz egy IDE-re, most a Visual Studio Code-t fogjuk használni: https://code.visualstudio.com/

Következő lépésként telepítsük fel a Node.js-t: https://nodejs.org/en/download/

A sikeres telepítést leellenőrizhetjük: node -v

Hasonlóan a Spring Boot-os alkalmazáshoz, itt is lesz egy dedikált fájl - package.json - amelybe definiálásra kerül(het) a build folyamat, illetve a projektünk függőségei.

A program felépítése, futtatása

A program felépítésének megértéséhez érdemes a Spring Boot alkalmazás felépítését is áttekinteni előzetesen, mivel bizonyos részek megegyeznek, és ezek nem lesznek ismét tárgyalva.

A programunk alapja a index.js fájl lesz, amely elindítja a lokális szervert a 8080-as porton: http://localhost:8080/

Az adatbáziskapcsolat létrehozásához a config/db.js fájlban kell beállítani a szükséges adatokat, amelyeket szintén a DigitalOcen PostgreSQL szolgáltatásából tudunk kiolvasni (lásd: PostgreSQL fejezet).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const pool = new Pool({
    user: 'USER',
    host: 'HOST',
    database: 'DATABASE',
    password: 'PASSWORD',
    port: 'PORT',
    ssl: {
      rejectUnauthorized: false
    }
  });

Ez a webalkalmazás is az MVC modelt követi, így a Controller komponenshez tartozó üzleti logikát a routes/route-dogs.js fájl tartalmazza. Amennyiben a domain nevünk (pl. valami.com vagy localhost:8080) után egy / jelet teszünk, akkor az ehhez a végponthoz tartozó függvény kerül végrehajtásra (egyébként ez az alapértelmett, tehát ha nem írjuk ki, akkor is a / útvonal töltődik be).

Ebben a függvényben meghívjuk a DogDAO().getDogs() függvényt, amely tartalmazza a tényleges SQL utasítást az adatbázisban található összes kutya lekéréséhez. Mivel a dogs táblában a tulajdonosának csak az azonosítója (owner_id) szerepel, ezért egy másik listában összegyűjtjük a felhasználók email címét is. A célunk az lesz, hogy alapesetben töltsük be és jelenítsük meg táblázatos formában az összes kutyát és az adatit. A két listát és az aktuális bejelentkezett felhasználót reprezentáló objektumot átadjuk a view-nak (jelen esetben a EJS utasításokkal kiegészített index.ejs fájlnak).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
router.get("/", async (req, res) => {
    let dogs = await new DogDAO().getDogs();
    var user_mails = [];
    const token = req.cookies.jwt
    var current_email;
    var current_role;

    if (token) {
        jwt.verify(token, jwtSecret, (err, decodedToken) => {
            current_email = decodedToken.email;
            current_role = decodedToken.role;
        })
    };

    for (const dog of dogs) {
        let user = await new UserDAO().getUserById(dog.owner_id);
        user_mails.push(user.email);
    };

    return res.render('index', {
        dogs: dogs,
        user_mails: user_mails,
        current_email: current_email,
        current_role: current_role
    });
});
Amennyiben a domain után írjuk a /add útvonalat, akkor az ehhez az útvonalhoz rendelt függvény kerül végrehajtásra. Két érték érkezett egy HTML form-ból, name és age, amely egy új kutyát reprezentál. Az adatbázisba történő mentéshez meghívjuk az DogDAO().createDog(..) metódust majd átirányítjuk az oldalt a /-hez tartozó nézetre. Node.js esetén a HTML form-ból érkező adatokat a req változóból érjük el (ami a request rövidítése, res pedig a response-é).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
router.post("/add", async (req, res) => {
    let name = req.body;
    let age = req.body;
    const token = req.cookies.jwt
    var email;

    if (token) {
        jwt.verify(token, jwtSecret, (err, decodedToken) => {
            email = decodedToken.email;
        })
    }

    let user = await new UserDAO().getUserByEmail(email);
    await new DogDAO().createDog(name, age, user.id);
    return res.redirect('/')
});

A tényleges SQL utasításokat a dao/dog-dao.js tartalmazza.

A megjelenítéséhez a views mappában található HTML fájlokat használjuk. A korábban említett index nézethez az index.ejs fájl tartozik. Az EJS konstrukcióit felhasználva tudjuk megjeleníteni a dinamikus tartalmat. Például a kutyák listázásához használt, a route-dogs.js fájlban a / végponthoz tartozó függvényben lévő model-hez rendeljük hozzá az eredményeket a dogs változóként (res.render('index',{ dogs : dogs }) függvénnyel). Ez tartalmazza az összes kutyát, amelyet aztán iteráljuk:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<% for (const dog of dogs) { %>
<% for (var i=0; i<dogs.length; i++) { %>
  <tr>
    <td><%= dogs[i].id %></td>
    <td><%= dogs[i].name %></td>
    <td><%= dogs[i].age %></td>
    <td><%= user_mails[i] %></td>
    <% if (current_email === user_mails[i] || current_role === 'ROLE_ADMIN') { %>
    <td><a  href="/edit/<%= dogs[i].id %>">Update</a></td>
    <% } %>
    <% if (current_role === 'ROLE_ADMIN') { %>
    <td><button data-toggle="modal" data-target="#modal-toTrash<%= dogs[i].id %>" class="btn-sm btn-dark details">Delete
    <span class="glyphicon glyphicon-trash" data-toggle="tooltip"
    data-placement="top" title="Sent to Trash Box"></span></button>
    </td>

    <% } %>
  ...
</tr>

A POST függvényhez tartozó HTML form megegyezik a Spring Boot leírásánál prezentált HTML kóddal.

A node telepítése során települ az npm is, azaz a node package manager (Java esetében a Maven-t az mvn parancs segítségével tudnánk használni terminálból).

A lokális futtatáshoz a következő utasításokat kell kiadnunk. Először a package.json fájlban definiált függőségeket kell telepíteni, ezeket a függőségéket egy node_modules mappába tárolja majd el (ezt a mappát mindenképpen adjuk hozzá a .gitignore fájlhoz):

npm install

Ezt követően, a következő utasítás hatására elindul egy ún. developer szerver, amely érzékeli a kódban a változtatásokat és újratölti a weboldalt:

npm run dev

A Node.js weboldal kinézete megegyezik a Spring Boot alkalmazás kinézetével:

dog-eclipse

Felhasználókezelés

A Spring Boot-hoz hasonlítva a felhasználókezelést, itt sokkal több minden marad a programozóra. A felhasználókezelést ebben a program a JWT (JSON Web Token) segítségével végezzük el, amit egy cookie-ban tárolunk. Első lépésként nézzük meg a config/auth.js fájlt. Az alábbi függvény ún. middleware-ként funkcionál, csak a ROLE_ADMIN és a ROLE_USER jogosultság esetén folytatja a futását (next()), egyébként hibát dob.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
userAuth = (req, res, next) => {
    const token = req.cookies.jwt

    if (token) {
        jwt.verify(token, jwtSecret, (err, decodedToken) => {
            if (err) {
                return res.status(401).json({
                    message: "Not authorized"
                })
            } else {
                if ((decodedToken.role !== "ROLE_USER") && (decodedToken.role !== "ROLE_ADMIN")) {
                    return res.status(401).json({
                        message: "Not authorized"
                    })
                } else {
                    next()
                }
            }
        })
    } else {
        return res
            .status(401)
            .json({
                message: "Not authorized, token not available"
            })
    }
}

Hasonlóan a Spring Boot alkalmazáshoz, itt is a /edit/2-szerű útvonalat szeretnénk levédeni, így a megfelelő Controller metódusba kell beleillesztenünk:

1
2
3
router.get("/edit/:id", userAuth, async (req, res) => {
  ...
}

Itt is igaz, hogy érdemes a HTML fájlokat áttekinteni, hogyan tudunk a bejelentkezett felhasználó jogosultságától függően HTML tag-eket elrejteni a view létrehozása során.

Az alkalmazások deploy-olása

Egy kitelepített mintaalkalmazás elérhető a következő címen: http://193.225.251.2:8080/

Az alkalmazás deploy-olása az a folyamat, amely során a szoftvert a fejlesztői környezet helyett (ahol lokális adatbázissal és a localhost-os címtartománnyal dolgozunk), egy éles környezetbe telepítjük és futtatjuk. Ehhez a legfontosabb lépések a következők:

Amennyiben az alkalmazásunkat szeretnénk éles környezetbe kitelepíteni és futtatni, akkor használjuk a következő utasításokat (Debian-alapú rendszeren):

Spring Boot deploy

apt update - lekérjük a csomagok legfrissebb verzióját

apt install maven -y - telepítjük a Maven-t (lokális fejlesztésnél az IDE intézte ezt nekünk)

apt install openjdk-11-jdk-headless -y - telepítjük a JDK-t

git clone https://git-okt.sed.inf.szte.hu/markusa/rf1-pelda - leklónozzuk a repót

cd rf1-pelda/ && git checkout java - a kód másik branch-en van, így váltunk

mvn clean install - készítünk egy futtatható állományt (egge)

java -jar target/demo-0.0.1-SNAPSHOT.jar & - elindítjuk a programot a háttérben, hogy a terminál bezárása után is fusson

Ha újabb verziót szeretnénk futtatni, akkor a következőt kell tennünk:

ps aux - megkeressük a java -jar... process azonosítóját (pl. 1234)

kill 1234 - leállítjuk, majd frissítjuk a repót és készítünk belőle ismét egy futtatható állományt (git pull, mvn clean install, java -jar..)

Node.js deploy

apt update - lekérjük a csomagok legfrissebb verzióját

apt install node -y - telepítjük a Node-t

apt install npm - telepítjük az NPM-et

git clone https://git-okt.sed.inf.szte.hu/markusa/rf1-pelda - leklónozzuk a repót

cd rf1-pelda/ && git checkout node - a kód másik branch-en van, így váltunk

npm install - készítünk egy futtatható állományt

node index.js & - elindítjuk a programot a háttérben, hogy a terminál bezárása után is fusson

Ha újabb verziót szeretnénk futtatni, akkor a következőt kell tennünk:

ps aux - megkeressük a node... process azonosítóját (pl. 1234)

kill 1234 - leállítjuk, majd frissítjuk a repót és készítünk belőle ismét egy futtatható állományt (git pull, npm install, node index..)

Kitelepítési lehetőségek

DigitalOcean

A DigitalOcean egy felhős infrastrukúra szolgáltató vállalat, amely IaaS (Infrastructure-as-a-Service) platformra specializálódott. IaaS szolgáltatás esetében virtuális erőforrások bérlésére van lehetőségünk, amelyet aztán tovább konfigurálhatunk a saját céljainkra. Esetünkben a DigitalOcean-tól bérelt erőforrások fogják a webszervert biztosítani, ehhez egy VPS-t, virtuális privát szervert kell majd igényelnünk, amit a DigitalOcean terminológiájában Droplet-nek neveznek. Ezenkívül lehetőségünk lesz adatbázis-szerver bérlésére is. A DigitalOcean-hoz hasonló szolgáltatások tipikusan a pay-as-you-go számlázást követik, amely annyit jelent, hogy csak az adott szolgáltatás használata után kell fizetni.

Az új felhasználók egy egyszeri, 200$ értékű kreditet kapnak, amelyet 60 napon keresztül vehetnek igénybe. A regisztrációhoz bankkártya megadása szükséges, azonban az esetlegesen előforduló kártyaterhelés sztornózva lesz.

A regisztrációt és a virtuális erőforrások létrehozását elegendő, ha csapatonként csak 1 fő végzi el. A szorgalmi időszak végével az erőforrások és a regisztráció törölhető. A 60 nap elteltével a futó szolgáltatások után díjat számít fel a rendszer a krediten felül.

Regisztráció

Nyissuk meg a következő oldalt: https://www.digitalocean.com/landing/do-for-higher-education

Regisztráció e-mail címmel:

mvc

Az egyetemi (stud-os) e-mail címet használjuk:

mvc

A regisztrációhoz érvényes bankkártya szükséges:

mvc

A kártyás tranzakció sztornózásra kerül:

mvc

E-mail-ben értesítenek, ha a felhasználó fiókhoz hozzárendelték a 200$ értékű kreditet:

mvc

Ezt követően, bejelentkezés után egy default first-project automatikusan létrehozásra kerül, ehhez fogunk tudni majd új szolgáltatásokat hozzáadni:

mvc

Az erőforrások létrehozásához nyissuk meg a DigitalOcean Control Panel-t: https://cloud.digitalocean.com/projects

Legegyszerűbben az adatbázis és a Droplet létrehozást a Create gombon keresztül tudjuk elérni:

mvc

PostgreSQL

Az alkalmazássablonok PostgreSQL-t használnak, de a DigitalOcean más adatbázisszolgáltatásokat is támogat. Válasszuk ki, hogy az európai szerverparkban hozza létre a PostgreSQL adatbázisunkat (pl. Frankfurt) és válasszuk ki a megfelelő árazást. Az adatbázis automatikusan kap egy egyedi nevet, ezt nem kell módosítani:

mvc

A létrehozás több időt is igénybe vehet (5-10 perc), várjuk meg még Running állapotba kerül az adatbázis. A telepítés állapotát az adatbázis neve alatt lévő loadbar segítségével figyelemmel követhetjük. Ahhoz, hogy a webalkalmazásunk csatlakozni tudjon ehhez az adatbázishoz, szükség van az adatbázis címére, nevére, felhasználónévre, jelszóra, és egy portszámra:

mvc

Ahhoz, hogy a lokális fejlesztés során elérjük az adatbázist, lokálisan is telepíteni kell a PostgreSQL-t: https://www.postgresql.org/download/

Droplet létrehozása és kitelepítés

A következő lépésben szeretnénk az alkalmazásunkat valós környezetbe kitelepíteni, azaz hogy ~bárhonnan elérhető legyen az oldal. Ehhez szükségünk lesz a DigitalOcean Droplet szolgáltatására:

mvc

Esetünkben egy Ubuntu alapú virtuális privát szervert választottunk - a legolcsóbb árazással - a frankfurti régióban. Válasszuk a jelszó alapú azonosítást, hagyjuk alapértelmezetten a VPS nevét és hozzuk létre a Droplet-et.

mvc

mvc

Ahhoz, hogy csatlakozni tudjunk SSH kapcsolaton keresztül a VPS-hez, szükségünk lesz az ipv4 címére.

mvc

Az Ubuntu alapú Droplet-ek esetén a felhasználónév a root lesz. A Droplet IP címének és a létrehozásakor megadott jelszó ismeretében csatlakozzunk a VPS-hez:

ssh root:jelszo@ip-cim

Microsoft Azure

https://azure.microsoft.com/en-us/free/students/

Heroku for GitHub Students

https://www.heroku.com/github-students

Oracle Cloud

https://www.oracle.com/cloud/free/

További tanácsok, linkek

Ahogyan az látható, bizonyos funkciók függőségekkel járnak, ezeket importálnunk kell (pom.xml, package.json). Érdemes a következő oldalakat is megismerni, amennyiben valamilyen további - nem alapértelmezett CRUD - funkciót akarunk megvalósítani, pl. automatikusan szeretnénk email-t küldeni egy sikeres regisztrációt követően:

Spring Boot: https://mvnrepository.com/artifact/org.apache.commons/commons-email

Node.js: https://www.npmjs.com/search?q=nodemailer

pgAdmin az adatbázis csatlakozáshoz: https://www.pgadmin.org/download/

Putty a távoló szerverhez/adatbázishoz való csatlakozáshoz: https://www.putty.org/


Utolsó frissítés: 2023-10-24 07:59:10