Custom Implementation Example
Dieser Leitfaden beschreibt den Aufbau einer vollständigen benutzerdefinierten Sicherheitsimplementierung mit sessionsbasierter Authentifizierung. 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 eine benutzerdefinierte Sicherheit nur, 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 Umleitungsorte
- SecurityContext - Verfolgt, wer in HTTP-Sitzungen angemeldet ist
- SecurityManager - Koordiniert Sicherheitsüberprüfungen und bietet Login/Logout
- SecurityRegistrar - Verbindet alles beim App-Start
Dieses Beispiel verwendet sessionsbasierten Speicher, aber Sie können dieselben Schnittstellen auch mithilfe von Datenbankabfragen, LDAP oder einem anderen Authentifizierungsbackend implementieren.
Wie die Teile zusammenarbeiten
Ablauf:
SecurityRegistrarläuft beim Start, erstellt den Manager, registriert Evaluatoren und attachiert den BeobachterSecurityManagerkoordiniert alles - es stellt den Kontext und die Konfiguration für die Evaluatoren bereitSecurityContextbeantwortet die Frage "Wer ist angemeldet?" durch Auslesen von HTTP-SitzungenSecurityConfigurationbeantwortet die Frage "Wohin umleiten?" für Anmeldeseiten und Seiten mit verweigertem ZugriffEvaluatorstreffen Zugriffsentscheidungen unter Verwendung des Kontexts und der Konfiguration
Schritt 1: Sicherheitskonfiguration definieren
Die Konfiguration gibt dem Sicherheitssystem vor, wie es sich verhalten soll und wo es die 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 standardmäßig eine Authentifizierung für alle Routen 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 Benutzerprincipal in der Sitzung vorhanden istgetPrincipal()ruft den Benutzernamen aus dem Sitzungs-Speicher abhasRole()überprüft, ob die Rollenset des Benutzers die angegebene Rolle enthältgetAttribute()/setAttribute()verwaltet benutzerdefinierte SicherheitsattributeEnvironment.getSessionAccessor()bietet thread-sicheren Sitzungszugriff
Schritt 3: Sicherheitsmanager erstellen
Der Manager koordiniert Sicherheitsentscheidungen. Er erweitert AbstractRouteSecurityManager, das Evaluator-Ketten und Zugriffsverweigerungen 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 löschen und zur Anmeldeseite weiterzuleiten - Verwendet
SessionObjectTablefür einfachen Sitzungs-Speicher - Speichert sich in
ObjectTablefür den Zugriff in der gesamten App
Schritt 4: Alles beim Start 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 Route-Sicherheitskomponenten während des Anwendungsstarts.
*
* <p>
* Richtet Sicherheitsmanager und Evaluatoren mit dem Router ein.
* </p>
*/
@AppListenerPriority(1)
public class SecurityRegistrar implements AppLifecycleListener {
/**
* {@inheritDoc}
*/
@Override
public void onWillRun(App app) {
// Sicherheitsmanager erstellen
SecurityManager securityManager = new SecurityManager();
securityManager.saveCurrent(securityManager);
// Eingebaute Evaluatoren mit Prioritäten registrieren
securityManager.registerEvaluator(new DenyAllEvaluator(), 0);
securityManager.registerEvaluator(new AnonymousAccessEvaluator(), 1);
securityManager.registerEvaluator(new PermitAllEvaluator(), 2);
securityManager.registerEvaluator(new RolesAllowedEvaluator(), 3);
// Sicherheitsbeobachter erstellen und an Router anhängen
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, damit er beim Start der App ausgeführt wird.
Wie es funktioniert:
- Läuft früh (
@AppListenerPriority(1)), um die Sicherheit einzurichten, bevor Routen geladen werden - Erstellt den Sicherheitsmanager und speichert ihn global
- Registriert eingebaute Evaluatoren in der Prioritätsreihenfolge (kleinere Zahlen werden zuerst ausgeführt)
- Erstellt den Beobachter, der die Navigation abfängt
- Hängt den Beobachter an den Router an, damit Sicherheitsüberprüfungen automatisch erfolgen
Nach diesem Vorgang ist die Sicherheit für alle Navigationen aktiv.
Verwendung Ihrer Implementierung
Erstellen Sie eine Anmeldesicht
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());
}
}