Custom Implementation Example
Esta guía detalla cómo construir una implementación de seguridad personalizada completa utilizando autenticación basada en sesiones. Aprenderás cómo las cuatro interfaces principales trabajan juntas al implementarlas desde cero.
La integración de Spring Security auto-configura todo lo que se muestra aquí. Solo construye una seguridad personalizada si tienes requisitos específicos o no estás usando Spring Boot.
Lo que construirás
Un sistema de seguridad funcional con cuatro clases:
- SecurityConfiguration - Define el comportamiento de seguridad y las ubicaciones de redirección
- SecurityContext - Realiza un seguimiento de quién ha iniciado sesión utilizando sesiones HTTP
- SecurityManager - Coordina las verificaciones de seguridad y proporciona inicio/cierre de sesión
- SecurityRegistrar - Conecta todo junto al iniciar la aplicación
Este ejemplo utiliza almacenamiento basado en sesiones, pero podrías implementar las mismas interfaces utilizando consultas a bases de datos, LDAP, o cualquier otro backend de autenticación.
Cómo funcionan las piezas juntas
Flujo:
SecurityRegistrarse ejecuta al inicio, crea el administrador, registra evaluadores y adjunta el observadorSecurityManagercoordina todo - proporciona el contexto y la configuración a los evaluadoresSecurityContextresponde "¿Quién ha iniciado sesión?" leyendo desde las sesiones HTTPSecurityConfigurationresponde "¿Dónde redirigir?" para las páginas de inicio de sesión y acceso denegadoEvaluatorstoman decisiones de acceso utilizando el contexto y la configuración
Paso 1: Definir la configuración de seguridad
La configuración indica al sistema de seguridad cómo comportarse y dónde redirigir a los usuarios:
package com.securityplain.security;
import com.webforj.router.history.Location;
import com.webforj.router.security.RouteSecurityConfiguration;
import java.util.Optional;
/**
* Configuración de seguridad para la aplicación.
*
* <p>
* Define dónde redirigir a los usuarios cuando se requiere autenticación o se deniega el acceso.
* </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- La seguridad está activaisSecureByDefault() = false- Las rutas son públicas a menos que se anoten (usatruepara requerir autenticación en todas las rutas por defecto)/login- Donde van los usuarios no autenticados/access-denied- Donde van los usuarios autenticados sin permisos
Paso 2: Implementar el contexto de seguridad
El contexto realiza un seguimiento de quién está conectado. Esta implementación utiliza sesiones HTTP para almacenar información del usuario:
Cómo funciona:
isAuthenticated()comprueba si existe un principal de usuario en la sesióngetPrincipal()recupera el nombre de usuario del almacenamiento de la sesiónhasRole()comprueba si el conjunto de roles del usuario contiene el rol especificadogetAttribute()/setAttribute()gestionan atributos de seguridad personalizadosEnvironment.getSessionAccessor()proporciona acceso seguro a las sesiones
Paso 3: Crear el administrador de seguridad
El administrador coordina las decisiones de seguridad. Extiende AbstractRouteSecurityManager, que maneja cadenas de evaluadores y deniegas de acceso:
Cómo funciona:
- Extiende
AbstractRouteSecurityManagerpara heredar la lógica de cadena de evaluadores - Proporciona implementaciones de
getConfiguration()ygetSecurityContext() - Añade
login()para autenticar usuarios y almacenar credenciales en la sesión - Añade
logout()para limpiar la sesión y redirigir a la página de inicio de sesión - Utiliza
SessionObjectTablepara almacenamiento simple de sesión - Se almacena en
ObjectTablepara acceso en toda la aplicación
Paso 4: Conectar todo al inicio
El registrador conecta todas las piezas cuando la aplicación se inicia:
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;
/**
* Registra componentes de seguridad de ruta durante el inicio de la aplicación.
*
* <p>
* Configura el administrador de seguridad y los evaluadores con el enrutador.
* </p>
*/
@AppListenerPriority(1)
public class SecurityRegistrar implements AppLifecycleListener {
/**
* {@inheritDoc}
*/
@Override
public void onWillRun(App app) {
// Crea el administrador de seguridad
SecurityManager securityManager = new SecurityManager();
securityManager.saveCurrent(securityManager);
// Registra evaluadores integrados con prioridades
securityManager.registerEvaluator(new DenyAllEvaluator(), 0);
securityManager.registerEvaluator(new AnonymousAccessEvaluator(), 1);
securityManager.registerEvaluator(new PermitAllEvaluator(), 2);
securityManager.registerEvaluator(new RolesAllowedEvaluator(), 3);
// Crea el observador de seguridad y adjunta al enrutador
RouteSecurityObserver securityObserver = new RouteSecurityObserver(securityManager);
Router router = Router.getCurrent();
if (router != null) {
router.getRenderer().addObserver(securityObserver);
}
}
}
Registrar el oyente:
Crea src/main/resources/META-INF/services/com.webforj.AppLifecycleListener con:
com.securityplain.security.SecurityRegistrar
Esto registra tu AppLifecycleListener para que se ejecute al inicio de la aplicación.
Cómo funciona:
- Se ejecuta temprano (
@AppListenerPriority(1)) para configurar la seguridad antes de que se carguen las rutas - Crea el administrador de seguridad y lo almacena globalmente
- Registra evaluadores integrados en orden de prioridad (números más bajos se ejecutan primero)
- Crea el observador que intercepta la navegación
- Adjunta el observador al enrutador para que las verificaciones de seguridad ocurran automáticamente
Después de que esto se ejecute, la seguridad está activa para toda la navegación.
Usando tu implementación
Crear una vista de inicio de sesión
La siguiente vista utiliza el componente Login.
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("Iniciar sesión")
@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());
}
}