Overslaan naar hoofdinhoud

Voorbeeld van aangepaste implementatie

Openen in ChatGPT

Deze gids doorloopt het bouwen van een complete aangepaste beveiligingsimplementatie met behulp van sessie-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 weergegeven. Bouw alleen aangepaste beveiliging als je specifieke vereisten hebt of geen gebruik maakt van Spring Boot.

Wat je gaat bouwen​

Een werkend beveiligingssysteem met vier klassen:

  • SecurityConfiguration - Definieert het beveiligingsgedrag en de omleidingslocaties
  • SecurityContext - Houdt bij wie is ingelogd met behulp van HTTP-sessies
  • SecurityManager - Coördineert beveiligingscontroles en biedt inloggen/afmelden aan
  • SecurityRegistrar - Verbindt alles met elkaar bij app-startup

Dit voorbeeld maakt gebruik van sessie-gebaseerde opslag, maar je zou dezelfde interfaces kunnen implementeren met behulp van database-query's, LDAP of een andere authenticatie-backend.

Hoe de onderdelen samenwerken​

Stroom:

  1. SecurityRegistrar draait bij opstarten, maakt de manager, 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 uit HTTP-sessies
  4. SecurityConfiguration beantwoordt "Waarheen omleiden?" voor inlog- en toegang geweigerd pagina's
  5. Evaluators nemen toegangsbeslissingen met behulp van de context en configuratie

Stap 1: Definieer beveiligingsconfiguratie​

De configuratie vertelt het beveiligingssysteem hoe het zich moet gedragen en waar het gebruikers naartoe 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 waar gebruikers naartoe 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 authenticatie op alle routes standaard te vereisen)
  • /login - Waar niet-geauthenticeerde gebruikers naartoe gaan
  • /access-denied - Waar geauthenticeerde gebruikers zonder machtigingen 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 sessie-gebaseerde beveiligingscontext.
*
* <p>
* Slaat gebruikersprincipal en rollen op in HTTP-sessie. Dit is een minimale implementatie voor leerdoeleinden.
* </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 er een gebruikersprincipal in de sessie bestaat
  • getPrincipal() haalt de gebruikersnaam uit de sessieopslag
  • hasRole() controleert of de rolset van de gebruiker de opgegeven rol bevat
  • getAttribute() / setAttribute() beheren aangepaste beveiligingsattributen
  • Environment.getSessionAccessor() biedt thread-veilige sessie-toegang

Stap 3: Maak beveiligingsmanager aan​

De manager coördineert beveiligingsbeslissingen. Het breidt AbstractRouteSecurityManager uit, dat evaluator ketens en toegang weigeringen afhandelt:

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 beveiligingsmanager.
*
* <p>
* Biedt statische methoden voor inloggen/afmelden en beheert beveiligingscontext.
* </p>
*/
public class SecurityManager extends AbstractRouteSecurityManager {

Hoe het werkt:

  • Breidt AbstractRouteSecurityManager uit om de evaluatorketenlogica over te nemen
  • Biedt implementaties van getConfiguration() en getSecurityContext()
  • Voegt login() toe om gebruikers te authenticeren en referenties in de sessie op te slaan
  • Voegt logout() toe om de sessie te wissen en door te verwijzen naar de inlogpagina
  • Gebruik SessionObjectTable voor eenvoudige sessieopslag
  • Slaat zichzelf op in ObjectTable voor toegang door de hele app

Stap 4: Verbind alles bij opstarten​

De registrar verbindt alle onderdelen wanneer de app opstart:

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 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 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 laden
  • Creëert de beveiligingsmanager en slaat deze globaal op
  • Registreert ingebouwde evaluators in prioriteitsvolgorde (lagere nummers draaien als eerste)
  • Creëert de observer die navigatie onderschept
  • Koppelt de observer aan de router zodat beveiligingscontroles automatisch plaatsvinden

Nadat dit draait, is de beveiliging actief voor alle navigatie.

Gebruik je implementatie​

Maak een inlogweergave aan​

De volgende weergave maakt gebruik van 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());
}
}