Custom Implementation Example
Dieser Leitfaden beschreibt den Aufbau einer vollständigen benutzerdefinierten Sicherheitsimplementierung unter Verwendung der sitzungsbasierten Authentifizierung. Sie werden lernen, wie die vier Kernschnittstellen zusammenarbeiten, indem Sie sie von Grund auf implementieren.
Die Spring Security Integration konfiguriert alles automatisch, was hier gezeigt wird. Erstellen Sie nur 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 Umleitungsstandorte
- SecurityContext - Verfolgt, wer mit HTTP-Sitzungen angemeldet ist
- SecurityManager - Koordiniert Sicherheitsprüfungen und ermöglicht Anmeldungen/Abmeldungen
- SecurityRegistrar - Verbindet alles beim Start der App
Dieses Beispiel verwendet sitzungsbasierten Speicher, aber Sie könnten dieselben Schnittstellen mit Datenbankabfragen, LDAP oder einem anderen Authentifizierungsbackend implementieren.
Wie die Teile zusammenarbeiten
Ablauf:
SecurityRegistrarläuft beim Start der App, erstellt den Manager, registriert die Evaluatoren und fügt den Observer hinzu.SecurityManagerkoordiniert alles - er versorgt die Evaluatoren mit Kontext und Konfiguration.SecurityContextbeantwortet die Frage "Wer ist angemeldet?" durch das Lesen der HTTP-Sitzungen.SecurityConfigurationbeantwortet die Frage "Wohin umleiten?" für Anmeldeseiten und Seiten mit verweigertem Zugriff.Evaluatorstreffen Zugriffsentscheidungen unter Verwendung des Kontexts und der Konfiguration.
Schritt 1: Sicherheitskonfiguration definieren
Die Konfiguration sagt dem Sicherheitssystem, wie es sich verhalten und wohin 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 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 standardmäßig für alle Routen zu verlangen)/login- Wo nicht authentifizierte Benutzer hingehen/access-denied- Wo authentifizierte Benutzer ohne Berechtigungen hingehen
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 Benutzerhaupt in der Sitzung existiertgetPrincipal()ruft den Benutzernamen aus dem Sitzungspeicher abhasRole()überprüft, ob die Rollenset des Benutzers die angegebene Rolle enthältgetAttribute()/setAttribute()verwalten benutzerdefinierte SicherheitsattributeEnvironment.getSessionAccessor()bietet thread-sicheren Zugriff auf die Sitzung
Schritt 3: Sicherheitsmanager erstellen
Der Manager koordiniert Sicherheitsentscheidungen. Er erweitert AbstractRouteSecurityManager, welches die Evaluatorenketten und den Zugriff verweigert:
Wie es funktioniert:
- Erweitert
AbstractRouteSecurityManager, um die Logik der Evaluatorenketten 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 umzuleiten - Verwendet
SessionObjectTablefür einfachen Sitzungspeicher - Speichert sich selbst in
ObjectTablefür den Zugriff aus der gesamten App
Schritt 4: Allen Komponenten beim Start verbinden
Der Registrar verbindet alle Teile, wenn die App startet:
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 Komponenten der Routen-Sicherheit während des Startvorgangs der Anwendung.
*
* <p>
* Richtet den Sicherheitsmanager und die Evaluatoren mit dem Router ein.
* </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 Sicherheitsbeobachter 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:
- Läuft früh (
@AppListenerPriority(1)), um Sicherheit einzurichten, bevor Routen geladen werden - Erstellt den Sicherheitsmanager und speichert ihn global
- Registriert integrierte Evaluatoren in der Prioritätsreihenfolge (niedrigere Nummern laufen zuerst)
- Erstellt den Observer, der die Navigation abfängt
- Hängt den Observer an den Router an, sodass Sicherheitsprüfungen automatisch durchgeführt werden
Nach dem Ausführen wird die Sicherheit für alle Navigationen aktiviert.
Verwendung Ihrer 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 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());
}
}