Siirry pääsisältöön

Custom Implementation Example

Avaa ChatGPT:ssä

Tämä opas käy läpi täydellisen mukautetun turvallisuusratkaisun rakentamisen istuntopohjaista todennusta käyttäen. Opit, kuinka neljä keskeistä rajapintaa toimii yhdessä toteuttamalla ne alusta alkaen.

Useimmissa sovelluksissa tulisi käyttää Spring Securityä

Spring Security -integraatio konfiguroi automaattisesti kaiken tämän. Rakenna mukautettu turvallisuus vain, jos sinulla on erityisiä vaatimuksia tai et käytä Spring Bootia.

Mitä rakennat

Toimiva turvallisuusjärjestelmä neljällä luokalla:

  • SecurityConfiguration - Määrittelee turvallisuuskäyttäytymisen ja uudelleenohjauspaikat
  • SecurityContext - Seuraa, kuka on kirjautuneena HTTP-istuntojen avulla
  • SecurityManager - Koordinoi turvallisuustarkastuksia ja tarjoaa kirjautumis-/uloskirjautumisratkaisuja
  • SecurityRegistrar - Yhdistää kaiken sovelluksen käynnistyksen yhteydessä

Tässä esimerkissä käytetään istuntopohjaista tallennusta, mutta voit toteuttaa samat rajapinnat käyttämällä tietokantakyselyitä, LDAP:ia tai mitä tahansa muuta todennusjärjestelmää.

Miten osat toimivat yhdessä

Virta:

  1. SecurityRegistrar toimii käynnistyksessä, luo hallitsijan, rekisteröi arvioijat ja liittää tarkkailijan
  2. SecurityManager koordinoi kaiken - se tarjoaa kontekstin ja konfiguraation arvioijille
  3. SecurityContext vastaa kysymykseen "Kuka on kirjautuneena?" lukemalla HTTP-istunnoista
  4. SecurityConfiguration vastaa kysymykseen "Minne ohjata?" kirjautumis- ja käyttöoikeuden hylkäämisen sivuille
  5. Evaluators tekevät käyttöoikeuspäätöksiä käyttäen kontekstia ja konfiguraatiota

Vaihe 1: Määrittele turvallisuuskonfiguraatio

Konfiguraatio kertoo turvallisuusjärjestelmälle, miten toimia ja minne ohjata käyttäjiä:

SecurityConfiguration.java
package com.securityplain.security;

import com.webforj.router.history.Location;
import com.webforj.router.security.RouteSecurityConfiguration;
import java.util.Optional;

/**
* Sovelluksen turvallisuuskonfiguraatio.
*
* <p>
* Määrittelee, minne ohjata käyttäjiä, kun todennusta vaaditaan tai pääsy hylätään.
* </p>
*/
public class SecurityConfiguration implements RouteSecurityConfiguration {

@Override
public boolean isEnabled() {
return true;
}

@Override
public boolean isSecureByDefault() {
return false;
}

@Override
public Optional<Location> getAuthenticationLocation() {
return Optional.of(new Location("/login"));
}

@Override
public Optional<Location> getDenyLocation() {
return Optional.of(new Location("/access-denied"));
}
}
  • isEnabled() = true - Turvallisuus on aktiivinen
  • isSecureByDefault() = false - Reitit ovat julkisia, ellei niitä ole merkitty (käytä true vaatiaksesi todennusta kaikilta reiteiltä oletuksena)
  • /login - Minne tunnistautumattomat käyttäjät menevät
  • /access-denied - Minne todennettujen käyttäjien, joilla ei ole lupia, tulee mennä

Vaihe 2: Toteuta turvallisuuskonteksti

Konteksti seuraa, kuka on kirjautuneena. Tämä toteutus käyttää HTTP-istuntoja käyttäjätietojen tallentamiseen:

SecurityContext.java
package com.securityplain.security;

import com.webforj.Environment;
import com.webforj.router.security.RouteSecurityContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
* Yksinkertainen istuntopohjainen turvallisuuskonteksti.
*
* <p>
* Tallentaa käyttäjäprincipaalin ja roolit HTTP-istuntoon. Tämä on minimaalinen toteutus opetustarkoituksiin.
* </p>
*/
public class SecurityContext implements RouteSecurityContext {
private static final String SESSION_USER_KEY = "security.user";
private static final String SESSION_ROLES_KEY = "security.roles";
private static final String SESSION_ATTRS_KEY = "security.attributes";

Miten se toimii:

  • isAuthenticated() tarkistaa, onko käyttäjäprincipaali olemassa istunnossa
  • getPrincipal() hakee käyttäjätunnuksen istuntovarastosta
  • hasRole() tarkistaa, sisältääkö käyttäjän roolijoukko määritellyn roolin
  • getAttribute() / setAttribute() hallinnoi mukautettuja turvallisuusattribuutteja
  • Environment.getSessionAccessor() tarjoaa säikeiden turvallisen pääsyn istuntoon

Vaihe 3: Luo turvallisuuden hallitsija

Hallitsija koordinoi turvallisuuspäätöksiä. Se laajentaa AbstractRouteSecurityManager-luokkaa, joka käsittelee arviointiketjuja ja käyttöoikeuden hylkäystä:

SecurityManager.java
package com.securityplain.security;

import com.webforj.environment.ObjectTable;
import com.webforj.environment.SessionObjectTable;
import com.webforj.router.Router;
import com.webforj.router.security.AbstractRouteSecurityManager;
import com.webforj.router.security.RouteAccessDecision;
import com.webforj.router.security.RouteSecurityConfiguration;
import com.webforj.router.security.RouteSecurityContext;

import java.util.Set;

/**
* Yksinkertainen turvallisuuden hallitsijatoteutus.
*
* <p>
* Tarjoaa staattisia metodeja kirjautumiseen/uloskirjautumiseen ja hallitsee turvallisuuskontextia.
* </p>
*/
public class SecurityManager extends AbstractRouteSecurityManager {

Miten se toimii:

  • Laajentaa AbstractRouteSecurityManager-luokkaa perimään arviointiketjun logiikka
  • Tarjoaa getConfiguration()- ja getSecurityContext()-toteutukset
  • Lisää login()-metodin käyttäjien todennusta varten ja tallentaa käyttöoikeudet istuntoon
  • Lisää logout()-metodin tyhjentämään istunnon ja ohjaamaan kirjautumissivulle
  • Käyttää SessionObjectTable yksinkertaista istuntovarastointia varten
  • Tallentaa itsensä ObjectTable sovelluksen laajuista pääsyä varten

Vaihe 4: Liitä kaikki käynnistyksessä

Rekisteröijä yhdistää kaikki osat, kun sovellus käynnistyy:

SecurityRegistrar.java
package com.securityplain.security;

import com.webforj.App;
import com.webforj.AppLifecycleListener;
import com.webforj.annotation.AppListenerPriority;
import com.webforj.router.Router;
import com.webforj.router.security.RouteSecurityObserver;
import com.webforj.router.security.evaluator.AnonymousAccessEvaluator;
import com.webforj.router.security.evaluator.DenyAllEvaluator;
import com.webforj.router.security.evaluator.PermitAllEvaluator;
import com.webforj.router.security.evaluator.RolesAllowedEvaluator;

/**
* Rekisteröi reitin turvallisuuskomponentit sovelluksen käynnistyksen aikana.
*
* <p>
* Asettuu turvallisuusmanageri ja arvioijat reitittimen kanssa.
* </p>
*/
@AppListenerPriority(1)
public class SecurityRegistrar implements AppLifecycleListener {

/**
* {@inheritDoc}
*/
@Override
public void onWillRun(App app) {
// Luo turvallisuusmanageri
SecurityManager securityManager = new SecurityManager();
securityManager.saveCurrent(securityManager);

// Rekisteröi sisäänrakennetut arvioijat prioriteetti järjestyksessä
securityManager.registerEvaluator(new DenyAllEvaluator(), 0);
securityManager.registerEvaluator(new AnonymousAccessEvaluator(), 1);
securityManager.registerEvaluator(new PermitAllEvaluator(), 2);
securityManager.registerEvaluator(new RolesAllowedEvaluator(), 3);

// Luo turvallisuustarkkailija ja liitä se reitittimeen
RouteSecurityObserver securityObserver = new RouteSecurityObserver(securityManager);
Router router = Router.getCurrent();
if (router != null) {
router.getRenderer().addObserver(securityObserver);
}
}
}

Rekisteröi kuuntelija:

Luo src/main/resources/META-INF/services/com.webforj.AppLifecycleListener sisältäen:

com.securityplain.security.SecurityRegistrar

Tämä rekisteröi AppLifecycleListener, jotta se suoritetaan sovelluksen käynnistyessä.

Miten se toimii:

  • Suorittaa aikaisin (@AppListenerPriority(1)) asettaakseen turvallisuuden ennen reittien lataamista
  • Luo turvallisuusmanagerin ja tallentaa sen globaalisti
  • Rekisteröi sisäänrakennetut arvioijat prioriteetti järjestyksessä (alac numerot suoritetaan ensin)
  • Luo tarkkailijan, joka puuttuu navigointiin
  • Liittää tarkkailijan reitittimeen, jotta turvallisuustarkastukset tapahtuvat automaattisesti

Kun tämä on suoritettu, turvallisuus on aktiivinen kaikissa navigoinneissa.

Käytä toteutustasi

Luo kirjautumisikkuna

Seuraava ikkuna käyttää Login -komponenttia.

LoginView.java
package com.securityplain.views;

import com.securityplain.security.SecurityManager;
import com.webforj.component.Composite;
import com.webforj.component.login.Login;
import com.webforj.router.Router;
import com.webforj.router.annotation.FrameTitle;
import com.webforj.router.annotation.Route;
import com.webforj.router.history.Location;
import com.webforj.router.security.annotation.AnonymousAccess;

@Route("/login")
@FrameTitle("Kirjaudu sisään")
@AnonymousAccess
public class LoginView extends Composite<Login> {
private final Login self = getBoundComponent();

public LoginView() {
self.onSubmit(e -> {
var result = SecurityManager.getCurrent().login(
e.getUsername(), e.getPassword()
);

if (result.isGranted()) {
Router.getCurrent().navigate(new Location("/"));
} else {
self.setError(true);
self.setEnabled(true);
}
});

self.whenAttached().thenAccept(c -> self.open());
}
}