Overslaan naar hoofdinhoud

Custom Implementation Example

Openen in ChatGPT

Deze gids behandelt het bouwen van een complete aangepaste beveiligingsimplementatie met op sessies gebaseerde authenticatie. Je leert hoe de vier kerninterfaces samenwerken door ze vanaf nul te implementeren.

De meeste apps zouden Spring Security moeten gebruiken

De Spring Security-integratie configureert automatisch alles wat hier wordt getoond. Bouw alleen aangepaste beveiliging als je specifieke vereisten hebt of geen gebruik maakt van Spring Boot.

Wat je zult bouwen

Een werkend beveiligingssysteem met vier klassen:

  • SecurityConfiguration - Definieert beveiligingsgedrag en omleidingslocaties
  • SecurityContext - Volgt wie is ingelogd met behulp van HTTP-sessies
  • SecurityManager - Coördineert beveiligingscontroles en biedt inloggen/uitloggen
  • SecurityRegistrar - Verbindt alles tijdens de opstart van de app

Dit voorbeeld gebruikt op sessies gebaseerde opslag, maar je zou dezelfde interfaces kunnen implementeren met behulp van databasequery's, LDAP of een andere authenticatiebackend.

Hoe de onderdelen samenwerken

Stroom:

  1. SecurityRegistrar draait bij opstart, maakt de manager aan, registreert evaluators en koppelt de observer
  2. SecurityManager coördineert alles - het biedt de context en configuratie aan evaluators
  3. SecurityContext beantwoordt "Wie is ingelogd?" door te lezen van HTTP-sessies
  4. SecurityConfiguration beantwoordt "Waarheen te omleiden?" voor inlog- en toegang geweigerd pagina's
  5. Evaluators maken toegangsbepalingen met behulp van de context en configuratie

Stap 1: Definieer beveiligingsconfiguratie

De configuratie geeft het beveiligingssysteem instructies over hoe het zich moet gedragen en waar het gebruikers heen moet omleiden:

SecurityConfiguration.java
package com.securityplain.security;

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

/**
* Beveiligingsconfiguratie voor de applicatie.
*
* <p>
* Definieert waarheen gebruikers moeten worden omgeleid wanneer authenticatie vereist is of toegang wordt geweigerd.
* </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 - Beveiliging is actief
  • isSecureByDefault() = false - Routes zijn openbaar, tenzij geannoteerd (gebruik true om standaard authenticatie voor alle routes te vereisen)
  • /login - Waar niet-geauthenticeerde gebruikers naartoe gaan
  • /access-denied - Waar geauthenticeerde gebruikers zonder toestemming naartoe gaan

Stap 2: Implementeer beveiligingscontext

De context houdt bij wie is ingelogd. Deze implementatie gebruikt HTTP-sessies om gebruikersinformatie op te slaan:

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;

/**
* Eenvoudige op sessies gebaseerde beveiligingscontext.
*
* <p>
* Slaat gebruikersprincipal en rollen op in HTTP-sessie. Dit is een minimale implementatie voor onderwijsdoeleinden.
* </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";

Hoe het werkt:

  • isAuthenticated() controleert of een gebruikersprincipal in de sessie bestaat
  • getPrincipal() haalt de gebruikersnaam uit de sessiestorage
  • hasRole() controleert of de rolenset van de gebruiker de opgegeven rol bevat
  • getAttribute() / setAttribute() beheren aangepaste beveiligingsattributen
  • Environment.getSessionAccessor() biedt thread-veilige toegang tot sessies

Stap 3: Maak beveiligingsmanager

De manager coördineert beveiligingsbeslissingen. Het breidt AbstractRouteSecurityManager uit, dat de evaluatorketens en toegang weigering behandelt:

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;

/**
* Eenvoudige implementatie van een beveiligingsmanager.
*
* <p>
* Biedt statische methoden voor inloggen/uitloggen en beheert de beveiligingscontext.
* </p>
*/
public class SecurityManager extends AbstractRouteSecurityManager {

Hoe het werkt:

  • Breidt AbstractRouteSecurityManager uit om de logica van de evaluator keten te erven
  • Biedt implementaties voor getConfiguration() en getSecurityContext()
  • Voegt login() toe om gebruikers te authentiseren en referenties in de sessie op te slaan
  • Voegt logout() toe om de sessie te wissen en om te leiden naar de inlogpagina
  • Gebruik SessionObjectTable voor eenvoudige sessieopslag
  • Slaat zichzelf op in ObjectTable voor toegang vanuit de hele app

Stap 4: Verbind alles bij de opstart

De registrar verbindt alle onderdelen wanneer de app wordt gestart:

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;

/**
* Registreert routebeveiligingscomponenten tijdens de opstart van de applicatie.
*
* <p>
* Stelt de beveiligingsmanager en evaluators in met de router.
* </p>
*/
@AppListenerPriority(1)
public class SecurityRegistrar implements AppLifecycleListener {

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

// Registreer ingebouwde evaluators met prioriteiten
securityManager.registerEvaluator(new DenyAllEvaluator(), 0);
securityManager.registerEvaluator(new AnonymousAccessEvaluator(), 1);
securityManager.registerEvaluator(new PermitAllEvaluator(), 2);
securityManager.registerEvaluator(new RolesAllowedEvaluator(), 3);

// Maak beveiligingsobserver aan en koppel aan router
RouteSecurityObserver securityObserver = new RouteSecurityObserver(securityManager);
Router router = Router.getCurrent();
if (router != null) {
router.getRenderer().addObserver(securityObserver);
}
}
}

Registreer de listener:

Maak src/main/resources/META-INF/services/com.webforj.AppLifecycleListener aan met:

com.securityplain.security.SecurityRegistrar

Dit registreert je AppLifecycleListener zodat deze draait bij de opstart van de app.

Hoe het werkt:

  • Draait vroeg (@AppListenerPriority(1)) om beveiliging in te stellen voordat routes worden geladen
  • Maakt de beveiligingsmanager aan en slaat deze wereldwijd op
  • Registreert ingebouwde evaluators in volgorde van prioriteit (lagere nummers draaien eerst)
  • Maakt de observer aan die navigatie onderschept
  • Koppelt de observer aan de router zodat beveiligingscontroles automatisch plaatsvinden

Nadat dit is uitgevoerd, is beveiliging actief voor alle navigatie.

Gebruik je implementatie

Maak een inlogweergave

De volgende weergave gebruikt de Login component.

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("Inloggen")
@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());
}
}