Meilleures pratiques pour l'authentification basée sur les jetons REST avec JAX-RS et Jersey


Je cherche un moyen d'activer l'authentification basée sur des jetons dans Jersey. J'essaie de ne pas utiliser un cadre particulier. Est-ce possible?

Mon plan est: Un utilisateur s'inscrit à mon service Web, mon service Web génère un jeton, l'envoie au client et le client le conservera. Ensuite, le client, pour chaque demande, enverra le jeton au lieu du nom d'utilisateur et du mot de passe.

Je pensais utiliser un filtre personnalisé pour chaque demande et @PreAuthorize("hasRole('ROLE')") mais j'ai juste pensé que cela provoque beaucoup de demandes à la base de données pour vérifier si le jeton est valide.

Ou ne pas créer de filtre et dans chaque requête mettre un jeton param? De sorte que chaque API vérifie d'abord le jeton et exécute ensuite quelque chose pour récupérer la ressource.

Author: cassiomolin, 2014-11-06

2 answers

Comment fonctionne l'authentification par jeton

Dans l'authentification basée sur un jeton, le client échange des informations d'identification matérielles (telles que le nom d'utilisateur et le mot de passe) contre une donnée appelée token. Pour chaque demande, au lieu d'envoyer les informations d'identification matérielles, le client enverra le jeton au serveur pour effectuer une authentification, puis une autorisation.

En quelques mots, un schéma d'authentification basé sur des jetons suit ces étapes:

  1. Le client envoie leurs informations d'identification (nom d'utilisateur et mot de passe) au serveur.
  2. Le serveur authentifie les informations d'identification et, si elles sont valides, générer un jeton pour l'utilisateur.
  3. Le serveur stocke le jeton généré précédemment dans un stockage avec l'identifiant de l'utilisateur et une date d'expiration.
  4. Le serveur envoie le jeton généré au client.
  5. Le client envoie le jeton au serveur dans chaque requête.
  6. Le serveur, dans chaque requête, extrait le jeton de la demande entrante. Avec le jeton, le serveur regarde les détails de l'utilisateur pour effectuer l'authentification.
    • Si le jeton est valide, le serveur accepte la demande.
    • Si le jeton n'est pas valide, le serveur refuse la demande.
  7. une Fois l'authentification effectuée, le serveur effectue d'autorisation.
  8. Le serveur peut fournir un point de terminaison pour actualiser les jetons.

Remarque: L'étape 3 n'est pas nécessaire si le serveur a émis un jeton signé (tel que JWT, qui vous permet d'effectuer une authentification sans état).

Ce que vous pouvez faire avec JAX-RS 2.0 (Jersey, RESTEasy et Apache CXF)

Cette solution utilise uniquement l'API JAX-RS 2.0, évitant toute solution spécifique au fournisseur. Donc, cela devrait fonctionner avec les implémentations JAX-RS 2.0, telles que Jersey, Il s'agit d'un logiciel de gestion de contenu et de gestion de contenu.

Il est utile de mentionner que si vous utilisez authentification basée sur un jeton, vous ne comptez pas sur les mécanismes de sécurité des applications Web Java EE standard proposés par le conteneur de servlet et configurables via le descripteur web.xml de l'application. C'est une authentification personnalisée.

Authentifier un utilisateur avec son nom d'utilisateur et son mot de passe et émettre un jeton

Créez une méthode de ressource JAX-RS qui reçoit et valide les informations d'identification (nom d'utilisateur et mot de passe) et émettez un jeton pour l'utilisateur:

@Path("/authentication")
public class AuthenticationEndpoint {

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response authenticateUser(@FormParam("username") String username, 
                                     @FormParam("password") String password) {

        try {

            // Authenticate the user using the credentials provided
            authenticate(username, password);

            // Issue a token for the user
            String token = issueToken(username);

            // Return the token on the response
            return Response.ok(token).build();

        } catch (Exception e) {
            return Response.status(Response.Status.FORBIDDEN).build();
        }      
    }

    private void authenticate(String username, String password) throws Exception {
        // Authenticate against a database, LDAP, file or whatever
        // Throw an Exception if the credentials are invalid
    }

    private String issueToken(String username) {
        // Issue a token (can be a random String persisted to a database or a JWT token)
        // The issued token must be associated to a user
        // Return the issued token
    }
}

Si des exceptions lors de la validation des informations d'identification, une réponse avec le statut 403 (Interdit) sera renvoyée.

Si les informations d'identification sont validées avec succès, une réponse avec le statut 200 (OK) sera retournée et le jeton émis sera envoyé au client dans la charge utile de réponse. Le client doit envoyer le jeton au serveur dans chaque requête.

Lors de la consommation de application/x-www-form-urlencoded , le client doit envoyer les informations d'identification au format suivant dans la demande charge utile:

username=admin&password=123456

Au lieu de form params, il est possible d'envelopper le nom d'utilisateur et le mot de passe dans une classe:

public class Credentials implements Serializable {

    private String username;
    private String password;

    // Getters and setters omitted
}

Puis consommez-le en JSON:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(Credentials credentials) {

    String username = credentials.getUsername();
    String password = credentials.getPassword();

    // Authenticate the user, issue a token and return a response
}

, en Utilisant cette approche, le client doit envoyer les informations d'identification dans le format suivant dans la charge utile de la demande:

{
  "username": "admin",
  "password": "123456"
}

Extraire le jeton de la requête et le valider

Le client doit envoyer le jeton dans l'en-tête HTTP Authorization standard de la requête. Pour exemple:

Authorization: Bearer <token-goes-here>

Le nom de l'en-tête HTTP standard est malheureux car il contient des informations authentication, pas authorization. Cependant, c'est l'en-tête HTTP standard pour l'envoi des informations d'identification au serveur.

JAX-RS fournit @NameBinding, une méta-annotation utilisée pour créer d'autres annotations pour lier des filtres et des intercepteurs à des classes de ressources et des méthodes. Définissez une annotation @Secured comme suit:

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }

La définition ci-dessus l'annotation de liaison de nom sera utilisée pour décorer une classe de filtre, qui implémente ContainerRequestFilter, vous permettant d'intercepter la requête avant qu'elle ne soit traitée par une méthode de ressource. Le ContainerRequestContext peut être utilisé pour accéder aux en-têtes de requête HTTP, puis extraire le jeton:

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private static final String REALM = "example";
    private static final String AUTHENTICATION_SCHEME = "Bearer";

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the Authorization header from the request
        String authorizationHeader =
                requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Validate the Authorization header
        if (!isTokenBasedAuthentication(authorizationHeader)) {
            abortWithUnauthorized(requestContext);
            return;
        }

        // Extract the token from the Authorization header
        String token = authorizationHeader
                            .substring(AUTHENTICATION_SCHEME.length()).trim();

        try {

            // Validate the token
            validateToken(token);

        } catch (Exception e) {
            abortWithUnauthorized(requestContext);
        }
    }

    private boolean isTokenBasedAuthentication(String authorizationHeader) {

        // Check if the Authorization header is valid
        // It must not be null and must be prefixed with "Bearer" plus a whitespace
        // The authentication scheme comparison must be case-insensitive
        return authorizationHeader != null && authorizationHeader.toLowerCase()
                    .startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
    }

    private void abortWithUnauthorized(ContainerRequestContext requestContext) {

        // Abort the filter chain with a 401 status code response
        // The WWW-Authenticate header is sent along with the response
        requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED)
                        .header(HttpHeaders.WWW_AUTHENTICATE, 
                                AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
                        .build());
    }

    private void validateToken(String token) throws Exception {
        // Check if the token was issued by the server and if it's not expired
        // Throw an Exception if the token is invalid
    }
}

Si des problèmes surviennent pendant la validation du jeton, une réponse avec le statut 401 (Non autorisé) sera renvoyée. Sinon la demande se poursuivra vers une ressource méthode.

Sécurisation de vos points de terminaison REST

Pour lier le filtre d'authentification aux méthodes de ressources ou aux classes de ressources, annotez-les avec l'annotation @Secured créée ci-dessus. Pour les méthodes et / ou les classes annotées, le filtre sera exécuté. Cela signifie que ces points de terminaison ne seront que atteints si la requête est exécutée avec un jeton valide.

Si certaines méthodes ou classes n'ont pas besoin d'authentification, il suffit de ne pas annoter ils:

@Path("/example")
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myUnsecuredMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // The authentication filter won't be executed before invoking this method
        ...
    }

    @DELETE
    @Secured
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response mySecuredMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured
        // The authentication filter will be executed before invoking this method
        // The HTTP request must be performed with a valid token
        ...
    }
}

Dans l'exemple ci-dessus, le filtre sera exécuté seulement de la mySecuredMethod(Long) méthode, car il est annoté avec @Secured.

Identification de l'utilisateur actuel

Il est très probable que vous ayez besoin de connaître l'utilisateur qui exécute la requête contre votre API REST. Les approches suivantes peuvent être utilisées:

Remplacer le contexte de sécurité de la requête en cours

Au Sein de votre ContainerRequestFilter.filter(ContainerRequestContext) méthode, une nouvelle SecurityContext l'instance peut être réglé pour la requête en cours. Puis remplacer la SecurityContext.getUserPrincipal(), de retour d'un Principal exemple:

final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {

        @Override
        public Principal getUserPrincipal() {
            return () -> username;
        }

    @Override
    public boolean isUserInRole(String role) {
        return true;
    }

    @Override
    public boolean isSecure() {
        return currentSecurityContext.isSecure();
    }

    @Override
    public String getAuthenticationScheme() {
        return AUTHENTICATION_SCHEME;
    }
});

Utiliser le jeton pour rechercher l'identificateur d'utilisateur (nom d'utilisateur), qui sera le Principal's nom.

Injecter le SecurityContext dans n'importe quelle classe de ressources JAX-RS:

@Context
SecurityContext securityContext;

La même chose peut être faite dans une méthode de ressource JAX-RS:

@GET
@Secured
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myMethod(@PathParam("id") Long id, 
                         @Context SecurityContext securityContext) {
    ...
}

Et puis obtenez le Principal:

Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();

Utilisation de CDI (Context and Dependency Injection)

Si, pour une raison quelconque, vous ne voulez pas remplacer le SecurityContext, vous pouvez utiliser CDI (Context and Dependency Injection), qui fournit des fonctionnalités utiles telles que les événements et les producteurs.

Créer un qualificateur CDI:

@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface AuthenticatedUser { }

Votre AuthenticationFilter créé ci-dessus, injecter une Event annoté avec @AuthenticatedUser:

@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;

Si l'authentification réussit, déclenchez le événement en passant le nom de l'utilisateur en paramètre (rappelez-vous, le jeton est émis pour un utilisateur et le jeton sera utilisé pour rechercher l'identificateur d'utilisateur):

userAuthenticatedEvent.fire(username);

Il est très probable qu'il existe une classe qui représente un utilisateur dans votre application. Appelons cette classe User.

Créez un bean CDI pour gérer l'événement d'authentification, recherchez une instance User avec le nom d'utilisateur correspondant et attribuez-la au champ producteur authenticatedUser:

@RequestScoped
public class AuthenticatedUserProducer {

    @Produces
    @RequestScoped
    @AuthenticatedUser
    private User authenticatedUser;

    public void handleAuthenticationEvent(@Observes @AuthenticatedUser String username) {
        this.authenticatedUser = findUser(username);
    }

    private User findUser(String username) {
        // Hit the the database or a service to find a user by its username and return it
        // Return the User instance
    }
}

Le champ authenticatedUser produit un User instance pouvant être injectée dans des beans gérés par conteneur, tels que les services JAX-RS, les beans CDI, les servlets et les EJB. Utilisez le morceau de code suivant pour injecter une instance User (en fait, c'est un proxy CDI):

@Inject
@AuthenticatedUser
User authenticatedUser;

Notez que le CDI @Produces l'annotation est différente à partir de JAX-RS @Produces annotation:

Assurez-vous d'utiliser le CDI @Produces annotation dans votre AuthenticatedUserProducer bean.

La clé ici est la fève annoté avec @RequestScoped, vous permettant de partager des données entre les filtres et les haricots. Si vous ne souhaitez pas utiliser d'événements, vous pouvez modifier le filtre pour stocker l'utilisateur authentifié dans un bean de portée de requête, puis le lire à partir de vos classes de ressources JAX-RS.

Par Rapport à l'approche qui remplace la SecurityContext, le CDI approche permet d'obtenir l'utilisateur authentifié à partir d' beans autres que les ressources et fournisseurs JAX-RS.

Prise en charge de l'autorisation basée sur les rôles

Veuillez vous référer à mon autre réponse pour plus de détails sur la prise en charge de l'autorisation basée sur les rôles.

Émission de jetons

Un jeton peut être:

  • Opaque: Ne révèle aucun détail autre que la valeur elle-même (comme une chaîne aléatoire)
  • Autonome: Contient des détails sur le jeton lui-même (comme JWT).

Voir les détails ci-dessous:

Chaîne aléatoire comme jeton

Un jeton peut être émis en générant une chaîne aléatoire et en la conservant dans une base de données avec l'identifiant de l'utilisateur et une date d'expiration. Un bon exemple de la façon de générer une chaîne aléatoire en Java peut être vu ici. Vous pouvez également utiliser:

Random random = new SecureRandom();
String token = new BigInteger(130, random).toString(32);

JWT (JSON Web Jeton)

JWT (JSON Web Token) est une méthode standard pour représenter les revendications en toute sécurité entre deux parties et est défini par le RFC 7519 .

C'est un jeton autonome et il vous permet de stocker des détails dans claims. Ces revendications sont stockées dans la charge utile du jeton qui est un JSON codé en Base64. Voici quelques revendications enregistrées dans la RFC 7519 et ce qu'elles signifient (lire la RFC complète pour plus de détails):

  • iss: Principal qui a émis le jeton.
  • sub: Principal c'est à dire le sous réserve de l'JWT.
  • exp: Date d'expiration du jeton.
  • nbf: Heure à laquelle le jeton commencera à être accepté pour le traitement.
  • iat: Heure à laquelle le jeton a été émis.
  • jti: Identifiant unique pour le jeton.

Sachez que vous ne devez pas stocker de données sensibles, telles que des mots de passe, dans le jeton.

La charge utile peut être lue par le client et l'intégrité du jeton peut être facilement vérifié en vérifiant sa signature sur le serveur. La signature est ce qui empêche le jeton d'être falsifié.

Vous n'aurez pas besoin de conserver les jetons JWT si vous n'avez pas besoin de les suivre. Bien que, en conservant les jetons, vous aurez la possibilité d'invalider et de révoquer l'accès de ceux-ci. Pour conserver la trace des jetons JWT, au lieu de conserver le jeton entier sur le serveur, vous pouvez conserver l'identifiant du jeton(jti réclamation) avec d'autres détails tels que l'utilisateur pour lequel vous avez émis le jeton, la date d'expiration, etc.

Lors de la persistance de jetons, envisagez toujours de supprimer les anciens afin d'empêcher votre base de données de se développer indéfiniment.

En utilisant JWT

Il existe quelques bibliothèques Java pour émettre et valider des jetons JWT tels que:

Pour trouver un autre grand ressources pour travailler avec JWT, jetez un oeil à http://jwt.io.

Gestion de la révocation de jetons avec JWT

Si vous voulez révoquer des jetons, vous devez en garder la trace. Vous n'avez pas besoin de stocker le jeton entier côté serveur, ne stockez que l'identifiant du jeton (qui doit être unique) et certaines métadonnées si vous en avez besoin. Pour l'identifiant de jeton, vous pouvez utiliser UUID.

Le jti la réclamation doit être utilisé pour stocker le jeton identifiant sur le jeton. Lors de la validation du jeton, assurez-vous qu'il n'a pas été révoqué en vérifiant la valeur du jti réclamation contre les identifiants de jeton que vous avez côté serveur.

Pour des raisons de sécurité, révoquez tous les jetons d'un utilisateur lorsqu'il modifie son mot de passe.

Informations complémentaires

  • Peu importe le type d'authentification que vous décidez d'utiliser. Toujours faites-le en haut d'une connexion HTTPS pour empêcher le L'attaque de l'homme du milieu .
  • Jetez un oeil àcette question de Sécurité de l'information pour plus d'informations sur les jetons.
  • Dans cet article, vous trouverez quelques informations utiles sur l'authentification par jeton.
 1442
Author: cassiomolin, 2020-01-15 14:42:43

Cette réponse est tout à propos de autorisation et il est un complément de ma réponse précédente sur authentification

Pourquoi autre réponse? J'ai tenté d'étendre ma réponse précédente en ajoutant des détails sur la prise en charge des annotations JSR-250. Cependant, la réponse originale est devenue le chemin trop longet a dépassé la longueur maximale de de 30 000 caractères. J'ai donc déplacé toute l'autorisation détails de cette réponse, en gardant l'autre réponse axée sur l'exécution de l'authentification et l'émission de jetons.


Prise en charge de l'autorisation basée sur les rôles avec l'annotation @Secured

En plus du flux d'authentification affiché dans l'autre réponse , l'autorisation basée sur les rôles peut être prise en charge dans les points de terminaison REST.

Créez une énumération et définissez les rôles en fonction de vos besoins:

public enum Role {
    ROLE_1,
    ROLE_2,
    ROLE_3
}

Modifier l'annotation de liaison de nom @Secured créée avant pour soutenir les rôles:

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
    Role[] value() default {};
}

Puis annoter les classes de ressources et les méthodes avec @Secured pour effectuer l'autorisation. Les annotations de méthode remplaceront les annotations de classe:

@Path("/example")
@Secured({Role.ROLE_1})
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // But it's declared within a class annotated with @Secured({Role.ROLE_1})
        // So it only can be executed by the users who have the ROLE_1 role
        ...
    }

    @DELETE
    @Path("{id}")    
    @Produces(MediaType.APPLICATION_JSON)
    @Secured({Role.ROLE_1, Role.ROLE_2})
    public Response myOtherMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured({Role.ROLE_1, Role.ROLE_2})
        // The method annotation overrides the class annotation
        // So it only can be executed by the users who have the ROLE_1 or ROLE_2 roles
        ...
    }
}

Créer un filtre avec le AUTHORIZATION la priorité, qui est exécutée après la AUTHENTICATION filtre de priorité défini précédemment.

Le ResourceInfo peut être utilisé pour obtenir la ressource Method et des ressources Class qui va gérer la demande et puis extraire les annotations @Secured d'eux:

@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the resource class which matches with the requested URL
        // Extract the roles declared by it
        Class<?> resourceClass = resourceInfo.getResourceClass();
        List<Role> classRoles = extractRoles(resourceClass);

        // Get the resource method which matches with the requested URL
        // Extract the roles declared by it
        Method resourceMethod = resourceInfo.getResourceMethod();
        List<Role> methodRoles = extractRoles(resourceMethod);

        try {

            // Check if the user is allowed to execute the method
            // The method annotations override the class annotations
            if (methodRoles.isEmpty()) {
                checkPermissions(classRoles);
            } else {
                checkPermissions(methodRoles);
            }

        } catch (Exception e) {
            requestContext.abortWith(
                Response.status(Response.Status.FORBIDDEN).build());
        }
    }

    // Extract the roles from the annotated element
    private List<Role> extractRoles(AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            return new ArrayList<Role>();
        } else {
            Secured secured = annotatedElement.getAnnotation(Secured.class);
            if (secured == null) {
                return new ArrayList<Role>();
            } else {
                Role[] allowedRoles = secured.value();
                return Arrays.asList(allowedRoles);
            }
        }
    }

    private void checkPermissions(List<Role> allowedRoles) throws Exception {
        // Check if the user contains one of the allowed roles
        // Throw an Exception if the user has not permission to execute the method
    }
}

Si l'utilisateur n'a pas l'autorisation d'exécuter l'opération, la demande est abandonnée avec un 403 (Interdit).

Savoir à l'utilisateur qui effectue la demande, voir ma réponse précédente. Vous pouvez l'obtenir du SecurityContext (qui devrait être déjà défini dans le ContainerRequestContext) ou injectez-le en utilisant CDI, selon l'approche que vous optez pour.

Si une annotation @Secured n'a aucun rôle déclaré, vous pouvez les utilisateurs authentifiés peuvent accéder à ce point de terminaison, sans tenir compte des rôles des utilisateurs.

Prise en charge de l'autorisation basée sur les rôles avec les annotations JSR-250

Alternativement à la définition des rôles dans l'annotation @Secured comme indiqué ci-dessus, vous pouvez considérer les annotations JSR-250 telles que @RolesAllowed, @PermitAll et @DenyAll.

JAX-RS ne prend pas en charge de telles annotations prêtes à l'emploi, mais cela pourrait être réalisé avec un filtre. Voici quelques considérations pour gardez à l'esprit si vous voulez soutenir tous:

Donc, un filtre d'autorisation qui vérifie les annotations JSR-250 pourrait être comme:

@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        Method method = resourceInfo.getResourceMethod();

        // @DenyAll on the method takes precedence over @RolesAllowed and @PermitAll
        if (method.isAnnotationPresent(DenyAll.class)) {
            refuseRequest();
        }

        // @RolesAllowed on the method takes precedence over @PermitAll
        RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
            return;
        }

        // @PermitAll on the method takes precedence over @RolesAllowed on the class
        if (method.isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // @DenyAll can't be attached to classes

        // @RolesAllowed on the class takes precedence over @PermitAll on the class
        rolesAllowed = 
            resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
        }

        // @PermitAll on the class
        if (resourceInfo.getResourceClass().isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // Authentication is required for non-annotated methods
        if (!isAuthenticated(requestContext)) {
            refuseRequest();
        }
    }

    /**
     * Perform authorization based on roles.
     *
     * @param rolesAllowed
     * @param requestContext
     */
    private void performAuthorization(String[] rolesAllowed, 
                                      ContainerRequestContext requestContext) {

        if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
            refuseRequest();
        }

        for (final String role : rolesAllowed) {
            if (requestContext.getSecurityContext().isUserInRole(role)) {
                return;
            }
        }

        refuseRequest();
    }

    /**
     * Check if the user is authenticated.
     *
     * @param requestContext
     * @return
     */
    private boolean isAuthenticated(final ContainerRequestContext requestContext) {
        // Return true if the user is authenticated or false otherwise
        // An implementation could be like:
        // return requestContext.getSecurityContext().getUserPrincipal() != null;
    }

    /**
     * Refuse the request.
     */
    private void refuseRequest() {
        throw new AccessDeniedException(
            "You don't have permissions to perform this action.");
    }
}

Remarque: ci-dessus mise en œuvre est basée sur le Maillot RolesAllowedDynamicFeature. Si vous utilisez Jersey, vous n'avez pas besoin d'écrire votre propre filtre, utilisez simplement l'implémentation existante.

 112
Author: cassiomolin, 2020-06-20 09:12:55