MODUL 7 - LEKCIJA 2

Registracija, Prijava i Autorizacija

Implementacija sigurnog procesa prijave, lockout mehanizma i Role-Based autorizacije

⏱️ Trajanje: ~3.5 časa 📚 Nivo: Srednji do Napredni 🎯 Praktični primjeri: 5

📖 Proces Prijave i Registracije

Implementacija sistema za kreiranje novih naloga i njihovu autentifikaciju. U sistemima javne uprave, registracija je često zatvorena (samo administratori kreiraju naloge), ali mehanizam "Lockout"-a prilikom prijave je od krucijalne važnosti za odbranu od Brute-Force pogađanja lozinki.

➕ Kontroler Registracije

Ovaj primjer prikazuje kreiranje korisnika uz pomoć UserManager-a, uz automatsku dodjelu početne uloge (Role).

🛠️ Register Action (AccountController.cs)
[HttpPost]
[AllowAnonymous] // Dozvoljeno svima da pristupe čak i ako je kontroler zaključan
[ValidateAntiForgeryToken]
public async Task Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        // 1. Priprema objekta baziranog na ViewModelu
        var user = new ApplicationUser { 
            UserName = model.Email, 
            Email = model.Email,
            Ime = model.Ime,
            Prezime = model.Prezime,
            JMBG = model.JMBG,
            DatumZaposlenja = DateTime.Now,
            OrganizacionaJedinicaId = model.SektorId
        };
        
        // 2. Kreiranje korisnika. Identity OVDE u pozadini vrši Hashiranje lozinke
        var result = await UserManager.CreateAsync(user, model.Password);
        
        if (result.Succeeded)
        {
            // 3. Dodjeljivanje osnovne role (ako RoleManager postoji)
            await UserManager.AddToRoleAsync(user.Id, "Sluzbenik");
            
            // Opcionalno: Šaljemo email za potvrdu računa (Email Confirmation)
            // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
            // ...
            
            // 4. Automatska prijava nakon registracije
            await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
            return RedirectToAction("Index", "Pocetna");
        }
        
        // Ako je puklo (npr. prekratka lozinka, email zauzet), dodajemo Identity greške u ModelState
        AddErrors(result);
    }
    // Ako ModelState nije validan, vrati formu nazad sa istim podacima
    return View(model);
}

🔐 Sistem Prijave sa Lockout Zaštitom

Kada se korisnik prijavljuje, SignInManager provjerava bazu, poredi Hash lozinke i izdaje Auth Cookie.

🛠️ Login Action
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid) return View(model);

    // shouldLockout: true je KRITIČNO ZBOG SIGURNOSTI!
    // Ako korisnik 5 puta (po defaultu) unese pogrešnu sifru, nalog se zaključava na 15 minuta.
    var result = await SignInManager.PasswordSignInAsync(
        model.Email, model.Password, model.RememberMe, shouldLockout: true);
        
    switch (result)
    {
        case SignInStatus.Success:
            // returnUrl ga vraća tamo gdje je prvobitno htio ići prije nego što je preusmjeren na login
            return RedirectToLocal(returnUrl); 
            
        case SignInStatus.LockedOut:
            // Preusmjerimo na poseban pogled koji kaže "Nalog je zaključan zbog sigurnosti"
            return View("Lockout");
            
        case SignInStatus.RequiresVerification:
            // Koristi se za 2FA (Two-Factor Authentication) npr. SMS kod
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            
        case SignInStatus.Failure:
        default:
            // NIKAD ne govorite "Lozinka je pogrešna". Recite "Neispravni podaci".
            // Ako kažete "Pogrešna lozinka", haker zna da email ustvari POSTOJI u bazi.
            ModelState.AddModelError("", "Neispravni podaci za prijavu.");
            return View(model);
    }
}

🛡️ Autorizacija (Role-based) putem filtera

Autentifikacija pita "Ko si ti?". Autorizacija pita "Imaš li dozvolu za ovo?".

ASP.NET MVC koristi [Authorize] filter. Ukoliko korisnik zatraži akciju za koju nema dozvolu, MVC ga automatski prebaci na Login ekran sa kodom 401.

🛠️ Atributska Autorizacija
// Pristup samo za prijavljene korisnike. 
// Gosti (Anonymous) ne mogu otvoriti Dashboard.
[Authorize]
public ActionResult Dashboard()
{
    return View();
}

// Pristup ima SAMO korisnik koji ima Role 'Administrator'.
// Uloga se čita direktno iz Auth Cookie-ja.
[Authorize(Roles = "Administrator")]
public ActionResult KreirajBudzet()
{
    return View();
}

// Pristup za više različitih uloga (bilo koja od ovih prolazi)
[Authorize(Roles = "Ministar, Inspektor, StrucniSavjetnik")]
public ActionResult PregledPovjerljivihDokumenata(int id)
{
    // Logika prikaza
}

🌍 Globalna Autorizacija (Best Practice za Javnu Upravu)

U nekim sistemima 99% aplikacije je tajno, a samo Login stranica je javna. Bolje je zaključati cijelu aplikaciju na nivou Global.asax / FilterConfig klase, nego pisati [Authorize] iznad svakog kontrolera!

🛠️ FilterConfig.cs
public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        
        // ZAKLJUČAJ CIJELU APLIKACIJU
        filters.Add(new AuthorizeAttribute()); 
    }
}

Kako onda pristupiti Login stranici?

Kada zaključate cijelu aplikaciju globalno, morate dodati atribut [AllowAnonymous] iznad AccountController-a ili iznad same Login akcije kako bi aplikacija uopšte omogućila korisniku da se prijavi.

✅ Zaključak

Sa Identity SignIn/User menadžerima kreiranje robusnog sistema prijave sa modernim sigurnosnim zahtjevima (kao što su Lockout i Password Hashing) se svodi na svega par linija koda, dok nam Role-based Authorization i [Authorize] filteri pružaju granularnu kontrolu pristupa državnim resursima aplikacije.