Custom Implementation Example
Dieser Leitfaden beschreibt, wie eine vollständige benutzerdefinierte Sicherheitsimplementierung mit sitzungsbasierter Authentifizierung erstellt wird. Sie werden lernen, wie die vier Kernschnittstellen zusammenarbeiten, indem Sie sie von Grund auf implementieren.
Die Spring Security-Integration konfiguriert automatisch alles, was hier gezeigt wird. Erstellen Sie nur dann eine benutzerdefinierte Sicherheit, wenn Sie spezifische Anforderungen haben oder Spring Boot nicht verwenden.
Was Sie erstellen werden
Ein funktionierendes Sicherheitssystem mit vier Klassen:
- SecurityConfiguration - Definiert das Sicherheitsverhalten und die Umleitungsziele
- SecurityContext - Verfolgt, wer mit HTTP-Sitzungen angemeldet ist
- SecurityManager - Koordiniert Sicherheitsprüfungen und bietet Anmelde-/Abmeldefunktionen
- SecurityRegistrar - Verbindet alles beim Start der App
Dieses Beispiel verwendet sitzungsbasierte Speicherung, aber Sie könnten die gleichen Schnittstellen mit Datenbankabfragen, LDAP oder jedem anderen Authentifizierungsbackend implementieren.
Wie die Teile zusammenarbeiten
Ablauf:
SecurityRegistrarläuft beim Start, erstellt den Manager, registriert Evaluatoren und hängt den Observer anSecurityManagerkoordiniert alles - er stellt den Kontext und die Konfiguration für die Evaluatoren zur VerfügungSecurityContextbeantwortet die Frage "Wer ist angemeldet?" durch Lesen aus HTTP-SitzungenSecurityConfigurationbeantwortet die Frage "Wohin umleiten?" für Anmelde- und Zugriff verweigert-SeitenEvaluatorstreffen Zugriffsentscheidungen mithilfe des Kontexts und der Konfiguration
Schritt 1: Sicherheitskonfiguration definieren
Die Konfiguration gibt dem Sicherheitssystem an, wie es sich verhalten und wohin es Benutzer umleiten soll:
package com.securityplain.security;
import com.webforj.router.history.Location;
import com.webforj.router.security.RouteSecurityConfiguration;
import java.util.Optional;
/**
* Sicherheitskonfiguration für die Anwendung.
*
* <p>
* Definiert, wohin Benutzer umgeleitet werden, wenn eine Authentifizierung erforderlich ist oder der Zugriff verweigert wird.
* </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- Sicherheit ist aktivisSecureByDefault() = false- Routen sind öffentlich, es sei denn, sie sind annotiert (verwenden Sietrue, um Authentifizierung für alle Routen standardmäßig zu verlangen)/login- Wohin nicht authentifizierte Benutzer gehen/access-denied- Wohin authentifizierte Benutzer ohne Berechtigungen gehen
Schritt 2: Sicherheitskontext implementieren
Der Kontext verfolgt, wer angemeldet ist. Diese Implementierung verwendet HTTP-Sitzungen, um Benutzerinformationen zu speichern:
Wie es funktioniert:
isAuthenticated()überprüft, ob ein Benutzerprinzipal in der Sitzung existiertgetPrincipal()ruft den Benutzernamen aus dem Sitzungs-speicher abhasRole()überprüft, ob das Rollenset des Benutzers die angegebene Rolle enthältgetAttribute()/setAttribute()verwalten benutzerdefinierte SicherheitsattributeEnvironment.getSessionAccessor()sorgt für einen thread-sicheren Zugriff auf die Sitzung
Schritt 3: Sicherheitsmanager erstellen
Der Manager koordiniert Sicherheitsentscheidungen. Er erweitert AbstractRouteSecurityManager, das Evaluator-Ketten und den Zugriffsverweigerungsprozess behandelt:
Wie es funktioniert:
- Erweitert
AbstractRouteSecurityManager, um die Logik der Evaluator-Kette zu erben - Bietet Implementierungen für
getConfiguration()undgetSecurityContext() - Fügt
login()hinzu, um Benutzer zu authentifizieren und Anmeldeinformationen in der Sitzung zu speichern - Fügt
logout()hinzu, um die Sitzung zu leeren und zur Anmeldeseite weiterzuleiten - Verwendet
SessionObjectTablefür die einfache Sitzungs-speicherung - Speichert sich selbst in
ObjectTablefür den Zugriff in der gesamten App
Schritt 4: Alles beim Starten verbinden
Der Registrar verbindet alle Teile, wenn die App gestartet wird:
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;
/**
* Registriert die Elemente der Routen-Sicherheit während des Anwendungsstarts.
*
* <p>
* Stellt den Sicherheitsmanager und Evaluatoren mit dem Router bereit.
* </p>
*/
@AppListenerPriority(1)
public class SecurityRegistrar implements AppLifecycleListener {
/**
* {@inheritDoc}
*/
@Override
public void onWillRun(App app) {
// Erstellen Sie den Sicherheitsmanager
SecurityManager securityManager = new SecurityManager();
securityManager.saveCurrent(securityManager);
// Registrieren Sie integrierte Evaluatoren mit Prioritäten
securityManager.registerEvaluator(new DenyAllEvaluator(), 0);
securityManager.registerEvaluator(new AnonymousAccessEvaluator(), 1);
securityManager.registerEvaluator(new PermitAllEvaluator(), 2);
securityManager.registerEvaluator(new RolesAllowedEvaluator(), 3);
// Erstellen Sie den Sicherheitsobserver und hängen Sie ihn an den Router an
RouteSecurityObserver securityObserver = new RouteSecurityObserver(securityManager);
Router router = Router.getCurrent();
if (router != null) {
router.getRenderer().addObserver(securityObserver);
}
}
}
Registrieren Sie den Listener:
Erstellen Sie src/main/resources/META-INF/services/com.webforj.AppLifecycleListener mit:
com.securityplain.security.SecurityRegistrar
Dies registriert Ihren AppLifecycleListener, sodass er beim Start der App ausgeführt wird.
Wie es funktioniert:
- Wird frühzeitig ausgeführt (
@AppListenerPriority(1)), um die Sicherheit einzurichten, bevor Routen geladen werden - Erstellt den Sicherheitsmanager und speichert ihn global
- Registriert integrierte Evaluatoren in der Prioritätsreihenfolge (niedrigere Zahlen werden zuerst ausgeführt)
- Erstellt den Observer, der die Navigation abfängt
- Hängt den Observer an den Router an, damit Sicherheitsüberprüfungen automatisch durchgeführt werden
Nach dem Ausführen dieses Codes ist die Sicherheit für alle Navigationen aktiv.
Verwenden Sie Ihre Implementierung
Erstellen Sie eine Anmeldeseite
Die folgende Ansicht verwendet die Login Komponente.
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("Anmeldung")
@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());
}
}