Overslaan naar hoofdinhoud

Custom Implementation Example

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