Custom Evaluators
Les évaluateurs personnalisés étendent le système de sécurité de webforJ avec une logique de contrôle d'accès spécialisée au-delà de l'authentification de base et des vérifications de rôle. Utilisez-les lorsque vous devez vérifier des conditions dynamiques qui dépendent du contexte de la requête, et pas seulement des autorisations d'utilisateur.
Ce guide couvre les évaluateurs personnalisés pour Spring Security. Si vous n'utilisez pas Spring Boot, consultez le guide de la chaîne d'évaluateurs pour comprendre comment fonctionnent les évaluateurs et Implémentation complète pour un exemple fonctionnel.
Qu'est-ce que des évaluateurs personnalisés
Un évaluateur détermine si un utilisateur peut accéder à une route spécifique en fonction de la logique personnalisée. Les évaluateurs sont vérifiés lors de la navigation avant que tout composant soit rendu, vous permettant d'intercepter et de contrôler l'accès de manière dynamique.
webforJ inclut des évaluateurs intégrés pour les annotations Jakarta standard :
AnonymousAccessEvaluator- Gère@AnonymousAccessPermitAllEvaluator- Gère@PermitAllRolesAllowedEvaluator- Gère@RolesAllowedDenyAllEvaluator- Gère@DenyAll
Les évaluateurs personnalisés suivent le même modèle, vous permettant de créer vos propres annotations et logique de contrôle d'accès.
Pour des détails sur @AnonymousAccess, @PermitAll, @RolesAllowed et @DenyAll, voir le guide des annotations de sécurité.
Cas d'utilisation : Vérification de propriété
Une exigence courante est de permettre aux utilisateurs d'accéder uniquement à leurs propres ressources. Par exemple, les utilisateurs ne devraient pouvoir modifier que leur propre profil, et non celui de quelqu'un d'autre.
Le problème : @RolesAllowed("USER") accorde l'accès à tous les utilisateurs authentifiés, mais ne vérifie pas si l'utilisateur accède à sa propre ressource. Vous devez comparer l'ID de l'utilisateur connecté avec l'ID de la ressource dans l'URL.
Scénario d'exemple :
- L'utilisateur ID
123est connecté - Il navigue vers
/users/456/edit - Peut-il accéder à cette page ? NON - il ne peut modifier que
/users/123/edit
Vous ne pouvez pas résoudre cela avec des rôles car cela dépend du paramètre de route :userId, qui change pour chaque requête.
Création d'une annotation personnalisée
Définissez une annotation pour marquer les routes nécessitant une vérification de propriété :
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireOwnership {
/**
* Le nom du paramètre de la route qui contient l'ID de l'utilisateur.
*/
String value() default "userId";
}
Utilisez-la sur les routes qui nécessitent des vérifications de propriété :
@Route(value = "/users/:userId/edit", outlet = MainLayout.class)
@RequireOwnership("userId")
public class EditProfileView extends Composite<Div> {
private final Div self = getBoundComponent();
public EditProfileView() {
self.setText("Page de modification du profil");
}
}
Implémentation de l'évaluateur
Créez un évaluateur géré par Spring qui compare l'ID de l'utilisateur connecté avec le paramètre de route :
@RegisteredEvaluator(priority = 10)
public class OwnershipEvaluator implements RouteSecurityEvaluator {
@Override
public boolean supports(Class<?> routeClass) {
return routeClass.isAnnotationPresent(RequireOwnership.class);
}
@Override
public RouteAccessDecision evaluate(Class<?> routeClass, NavigationContext context,
RouteSecurityContext securityContext, SecurityEvaluatorChain chain) {
// D'abord vérifier l'authentification
if (!securityContext.isAuthenticated()) {
return RouteAccessDecision.denyAuthentication();
}
// Obtenir l'annotation
RequireOwnership annotation = routeClass.getAnnotation(RequireOwnership.class);
String paramName = annotation.value();
// Obtenir l'ID de l'utilisateur connecté à partir du contexte de sécurité
String currentUserId = securityContext.getPrincipal()
.filter(p -> p instanceof UserDetails)
.map(p -> ((UserDetails) p).getUsername())
.orElse(null);
// Obtenir :userId à partir des paramètres de la route
String requestedUserId = context.getRouteParameters()
.get(paramName)
.orElse(null);
// Vérifier s'ils correspondent
if (currentUserId != null && currentUserId.equals(requestedUserId)) {
// Propriété vérifiée - continuer la chaîne pour permettre à d'autres évaluateurs
return chain.evaluate(routeClass, context, securityContext);
}
return RouteAccessDecision.deny("Vous ne pouvez accéder qu'à vos propres ressources");
}
}
Spring découvre automatiquement et enregistre les évaluateurs annotés avec @RegisteredEvaluator.
Comment cela fonctionne
L'implémentation de l'évaluateur a deux méthodes clés :
supports(Class<?> routeClass)
- Renvoie
truesi cet évaluateur doit traiter la route - Seuls les évaluateurs qui renvoient
trueseront invoqués pour la route - Filtre les routes en vérifiant la présence de l'annotation
@RequireOwnership
evaluate(...)
- Vérifie d'abord si l'utilisateur est authentifié
- Obtient l'ID de l'utilisateur connecté à partir de
securityContext.getPrincipal() - Obtient la valeur du paramètre de route à partir de
context.getRouteParameters().get(paramName) - Compare les deux ID
- S'ils correspondent, délègue à
chain.evaluate()pour permettre l'exécution d'autres évaluateurs - S'ils ne correspondent pas, renvoie
deny()avec une raison
Exemple de flux
Lorsque la vérification de propriété échoue :
- L'utilisateur
123se connecte et navigue vers/users/456/edit OwnershipEvaluator.supports()renvoietrue(la route a@RequireOwnership)OwnershipEvaluator.evaluate()s'exécute :currentUserId = "123"(à partir du contexte de sécurité)requestedUserId = "456"(à partir du paramètre de route:userId)"123".equals("456")→false- Renvoie
RouteAccessDecision.deny("Vous ne pouvez accéder qu'à vos propres ressources")
- L'utilisateur est redirigé vers une page d'accès refusé
Lorsque la vérification de propriété réussit :
- L'utilisateur
123se connecte et navigue vers/users/123/edit OwnershipEvaluator.evaluate()s'exécute :currentUserId = "123",requestedUserId = "123"- Les ID correspondent → appelle
chain.evaluate()pour continuer
- Si aucun autre évaluateur ne refuse l'accès, l'utilisateur se voit accorder l'accès
Comprendre la chaîne d'évaluateurs
Le système de sécurité utilise un modèle de chaîne de responsabilité où les évaluateurs sont traités par ordre de priorité. Les évaluateurs peuvent soit prendre des décisions terminales, soit déléguer à la chaîne pour combiner plusieurs vérifications.
Comment la chaîne fonctionne
- Les évaluateurs sont triés par priorité (nombres plus bas en premier)
- Pour chaque évaluateur,
supports(routeClass)est appelé pour vérifier s'il s'applique - Si
supports()renvoietrue, la méthodeevaluate()de l'évaluateur est appelée - L'évaluateur peut soit :
- Retourner une décision terminale (
grant()oudeny()) - interrompt la chaîne - Délègue à la chaîne en appelant
chain.evaluate()- permet à d'autres évaluateurs de s'exécuter
- Retourner une décision terminale (
- Si la chaîne se termine sans décision et que la sécurité par défaut est activée, les utilisateurs non authentifiés sont refusés