Dans cet article, on va s’intéresser au module “Security” d’Apache Deltaspike et la sécurisation d’appels CRUD.
Concepts
Apache Deltaspike Security propose 3 mécanismes classiques, à savoir:
- Gestion de l’identité
- Gestion des rôles
- Gestion des permissions
Gestion de l’identité
Pour confirmer l’identité d’un utilisateur, Deltaspike met en œuvre un mécanisme de contrôle basé sur l’annotation @Secures
. Cette annotation doit être complétée par une annotation personnalisée permettant de choisir le contrôle à effectuer (donc on peut avoir avoir plusieurs contrôles. On pourrait imaginer des URI réservées aux utilisateurs d’une branche particulière d’un LDAP mais interdites aux autres branches).
Gestion des rôles
Une fois l’identité confirmée, Deltaspike propose de vérifier les rôles de l’utilisateur. Par rôle, on entend la notion de fonction (ex: l’utilisateur est-il responsable d’équipe?). Souvent cette notion est traduite en groupes dans un LDAP. Bien sûr, la vérification des rôles est réalisée après la confirmation d’identité.
Gestion des permissions
En complément des deux concepts précédents, Deltaspike permet de gérer des droits fins. En effet, on va pouvoir configurer chaque URI avec un ou plusieurs droits supplémentaires. Par exemple, on peut imaginer que le responsable d’une équipe et son adjoint (qui n’est pas responsable) peuvent déposer des rapports, tandis que les autres membres de l’équipe peuvent uniquement lire ces rapports.
Exemple complet
Alice, Bob et Domi sont membres de l’équipe “IT Dev”. Alice en est la responsable et Bob est son adjoint.
Dans le LDAP, on trouve trois groupes:
- “IT Dev”, qui contient “IT Dev Resp” et “IT Dev Members”.
- Alice est membre de “IT Dev Resp” puisqu’elle est responsable de l’équipe.
- Bob et Domi sont membres de “IT Dev Members”.
- Alice et Bob ont la permission “Dépôt”, que Domi ne possède pas.
L’application propose l’URL “/rapport” en GET et POST.
- GET est accessible au groupe “IT Dev”
- POST est accessible à la permission “Dépôt”
Mise en œuvre
Ajout du module
Pour ajouter Deltaspike-Security, il faut inclure deux dépendances (version 1.9.5 lors de la rédaction) au fichier pom.xml du projet:
1 <dependency>
2 <groupId>org.apache.deltaspike.modules</groupId>
3 <artifactId>deltaspike-security-module-api</artifactId>
4 <version>${deltaspike.version}</version>
5 <scope>compile</scope>
6 </dependency>
7 <dependency>
8 <groupId>org.apache.deltaspike.modules</groupId>
9 <artifactId>deltaspike-security-module-impl</artifactId>
10 <version>${deltaspike.version}</version>
11 <scope>runtime</scope>
12 </dependency>
Et c’est tout. Facile.
Contrôle de l’identité
Ici on va ajouter trois classes et deux annotations qui interagissent via l’injection de dépendances.
LfdAuthorizer
Cette classe est chargée de contrôler l’identité de l’utilisateur. Ici, je me contente de vérifier que l’utilisateur possède une session.
1@ApplicationScopedpublic class LfdAuthorizer {
2 @Inject private SessionService sessionService;
3
4 @Secures
5 @LfdSecured
6 public boolean doSecuredCheck(InvocationContext invocationContext, BeanManager manager,
7 @LfdUserLogged LfdUser user) throws Exception {
8 return ((null != user) && (this.sessionService.hasSession(user.getUser())));
9 }
10}
@LfdSecured
Cette annotation permet d’indiquer que la méthode est réservée aux utilisateurs identifiés.
@LfdUserLogged
Cette annotation permet de récupérer l’identifiant de l’utilisateur effectuant la requête.
LfdUserBean
1@SessionScoped
2@Named
3public class LfdUserBean implements Serializable {
4 @Inject private LfdUser user;
5
6 public LfdUser getUser() { return user; }
7 public void setUser(LfdUser user) { this.user = user; }
8
9 @Produces
10 @LfdUserLogged
11 @RequestScoped
12 public LfdUser getLfdUser() { return this.user; }
13}
C’est finalement ce bean qui est responsable de la magie: il stocke le nom de l’utilisateur et permet son injection via l’annotation @LfdUserLogged
.
@LfdSecuredUrl
En l’état cette classe ne fait rien mais c’est elle qui doit vérifier les rôles et permissions de l’utilisateur.
Utilisation dans le processus de connexion
Lors de l’appel de l’url “/login” (note: cette url n’est pas sécurisée, bien sûr), on va appeler le service qui gère les utilisateurs (ici UserService
, original en diable). La méthode loginUser
reçoit donc le nom et le mot de passe de l’utilisateur (ici je n’ai pas écrit comment vérifier le mot de passe, ce n’est pas le sujet de l’article). Le point important est “this.lfdUserBean.setUser(new LfdUser(username));”: c’est cette ligne qui va permettre de conserver le nom de l’utilisateur pour les prochaines requêtes et permettre de l’injecter via @LfdUserLogged
.
1@ApplicationScoped
2@Transactional
3public class UserService {
4 @Inject private SessionService sessionService;
5 @Inject private LfdUserBean lfdUserBean;
6
7 @Transactional(Transactional.TxType.REQUIRED)
8 public Boolean loginUser(String username, String pwd) {
9 this.sessionService.create(username);
10 this.lfdUserBean.setUser(new LfdUser(username));
11 return Boolean.TRUE;
12 }
13}
Utilisation dans le processus de déconnexion
Ici, au contraire du processus de connexion, l’url de déconnexion doit être sécurisée. On utilise l’annotation @LfdSecured
en combinaison avec @Secures(LfdSecuredUrl.class)
.
1@RequestScoped
2@Path("/user")
3@Named
4public class UserController {
5
6 @Inject private UserService userService;
7 @Inject private SessionService sessionService;
8
9 @DELETE
10 @Path("/logout")
11 @Consumes(MediaType.APPLICATION_JSON)
12 @Produces(MediaType.APPLICATION_JSON)
13 @Secured(LfdSecuredUrl.class)
14 @LfdSecured
15 public Response logoutUser(JsonUserLogout nameLog) {
16 this.userService.logoffUser(nameLog.getUsername());
17 nameLog.setExited(true);
18 return Response.status(Response.Status.OK).entity(nameLog).build();
19 }
20}
Détails du fonctionnement
Lors de l’appel de l’url “/logout”, Deltaspike va déclencher deux actions. La première est d’appeler la méthode LfdAuthorizer.doSecuredCheck
(grâce au lien réalisée par l’annotation @LfdSecured
présente sur la méthode logoutUser
et doSecuredCheck
). La deuxième est d’appeler la méthode LfdSecuredUrl.checkPermission
en utilisant la classe indiquée dans l’annotation @Secures
.
Conclusion
Malgré une documentation limitée et datée (utilisation de classes dépréciées par exemple), Apache Deltaspike propose un mécanisme de sécurisation plutôt simple à mettre en œuvre et offrant énormément de souplesse.