Best practice per l'autenticazione basata su token REST con JAX-RS e Jersey


459

Sto cercando un modo per abilitare l'autenticazione basata su token in Jersey. Sto cercando di non usare alcun quadro particolare. È possibile?

Il mio piano è: un utente si iscrive al mio servizio Web, il mio servizio Web genera un token, lo invia al client e il client lo manterrà. Quindi il client, per ogni richiesta, invierà il token anziché nome utente e password.

Stavo pensando di utilizzare un filtro personalizzato per ogni richiesta, @PreAuthorize("hasRole('ROLE')") ma ho pensato che questo causasse molte richieste al database per verificare se il token è valido.

O non creare un filtro e in ogni richiesta inserire un token param? In modo che ogni API controlli prima il token e dopo esegua qualcosa per recuperare la risorsa.

Risposte:


1388

Come funziona l'autenticazione basata su token

Nell'autenticazione basata su token, il client scambia credenziali rigide (come nome utente e password) con un pezzo di dati chiamato token . Per ogni richiesta, invece di inviare le credenziali effettive, il client invierà il token al server per eseguire l'autenticazione e quindi l'autorizzazione.

In poche parole, uno schema di autenticazione basato su token segue questi passaggi:

  1. Il client invia le proprie credenziali (nome utente e password) al server.
  2. Il server autentica le credenziali e, se sono valide, genera un token per l'utente.
  3. Il server memorizza il token generato in precedenza in un certo spazio di archiviazione insieme all'identificatore utente e una data di scadenza.
  4. Il server invia il token generato al client.
  5. Il client invia il token al server in ogni richiesta.
  6. Il server, in ogni richiesta, estrae il token dalla richiesta in arrivo. Con il token, il server cerca i dettagli dell'utente per eseguire l'autenticazione.
    • Se il token è valido, il server accetta la richiesta.
    • Se il token non è valido, il server rifiuta la richiesta.
  7. Una volta eseguita l'autenticazione, il server esegue l'autorizzazione.
  8. Il server può fornire un endpoint per aggiornare i token.

Nota: il passaggio 3 non è necessario se il server ha emesso un token firmato (come JWT, che consente di eseguire l' autenticazione senza stato ).

Cosa puoi fare con JAX-RS 2.0 (Jersey, RESTEasy e Apache CXF)

Questa soluzione utilizza solo l'API JAX-RS 2.0, evitando qualsiasi soluzione specifica del fornitore . Quindi, dovrebbe funzionare con le implementazioni JAX-RS 2.0, come Jersey , RESTEasy e Apache CXF .

Vale la pena ricordare che se si utilizza l'autenticazione basata su token, non si fa affidamento sui meccanismi di sicurezza delle applicazioni Web Java EE standard offerti dal contenitore servlet e configurabili tramite il web.xmldescrittore dell'applicazione . È un'autenticazione personalizzata.

Autenticazione di un utente con nome utente e password ed emissione di un token

Creare un metodo di risorsa JAX-RS che riceve e convalida le credenziali (nome utente e password) ed emette un token per l'utente:

@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
    }
}

Se vengono generate eccezioni durante la convalida delle credenziali, 403verrà restituita una risposta con lo stato (Non consentito).

Se le credenziali vengono convalidate correttamente, 200verrà restituita una risposta con lo stato (OK) e il token emesso verrà inviato al client nel payload della risposta. Il client deve inviare il token al server in ogni richiesta.

Quando consuma application/x-www-form-urlencoded, il client deve inviare le credenziali nel seguente formato nel payload della richiesta:

username=admin&password=123456

Invece di parametri param, è possibile racchiudere il nome utente e la password in una classe:

public class Credentials implements Serializable {

    private String username;
    private String password;

    // Getters and setters omitted
}

E poi consumalo come 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
}

Utilizzando questo approccio, il client deve inviare le credenziali nel seguente formato nel payload della richiesta:

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

Estrarre il token dalla richiesta e convalidarlo

Il client deve inviare il token Authorizationnell'intestazione HTTP standard della richiesta. Per esempio:

Authorization: Bearer <token-goes-here>

Il nome dell'intestazione HTTP standard è sfortunato perché contiene informazioni di autenticazione , non di autorizzazione . Tuttavia, è l'intestazione HTTP standard per l'invio di credenziali al server.

JAX-RS fornisce @NameBindinguna meta-annotazione utilizzata per creare altre annotazioni per associare filtri e intercettori a classi e metodi delle risorse. Definire @Securedun'annotazione come segue:

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

L'annotazione di associazione dei nomi sopra definita verrà utilizzata per decorare una classe di filtro, che implementa ContainerRequestFilter, consentendo di intercettare la richiesta prima che venga gestita da un metodo di risorsa. È ContainerRequestContextpossibile utilizzare per accedere alle intestazioni della richiesta HTTP e quindi estrarre il token:

@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
    }
}

Se si verificano problemi durante la convalida del token, 401verrà restituita una risposta con lo stato (Non autorizzato). Altrimenti la richiesta passerà a un metodo di risorsa.

Protezione degli endpoint REST

Per associare il filtro di autenticazione a metodi o classi di risorse, annotarli con l' @Securedannotazione creata sopra. Per i metodi e / o le classi che sono annotati, il filtro verrà eseguito. Significa che tali endpoint saranno raggiunti solo se la richiesta viene eseguita con un token valido.

Se alcuni metodi o classi non necessitano di autenticazione, semplicemente non annotarli:

@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
        ...
    }
}

Nell'esempio mostrato sopra, il filtro verrà eseguito solo per il mySecuredMethod(Long)metodo perché è annotato con @Secured.

Identificazione dell'utente corrente

È molto probabile che dovrai conoscere nuovamente l'utente che sta eseguendo la richiesta tramite l'API REST. I seguenti approcci possono essere utilizzati per raggiungerlo:

Sostituzione del contesto di sicurezza della richiesta corrente

Nel tuo ContainerRequestFilter.filter(ContainerRequestContext)metodo, è SecurityContextpossibile impostare una nuova istanza per la richiesta corrente. Quindi sovrascrivere il SecurityContext.getUserPrincipal(), restituendo Principalun'istanza:

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;
    }
});

Utilizzare il token per cercare l'identificatore utente (nome utente), che sarà il Principalnome.

Iniettare SecurityContextin qualsiasi classe di risorse JAX-RS:

@Context
SecurityContext securityContext;

Lo stesso può essere fatto in un metodo di risorsa JAX-RS:

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

E poi ottieni il Principal:

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

Utilizzo di CDI (Iniezione di contesto e dipendenza)

Se, per qualche motivo, non si desidera ignorare SecurityContext, è possibile utilizzare CDI (Context and Dependency Injection), che fornisce funzionalità utili come eventi e produttori.

Creare un qualificatore CDI:

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

Nel AuthenticationFiltercreato sopra, iniettare un Eventannotato con @AuthenticatedUser:

@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;

Se l'autenticazione ha esito positivo, attiva l'evento che passa il nome utente come parametro (ricorda, il token viene emesso per un utente e il token verrà utilizzato per cercare l'identificatore utente):

userAuthenticatedEvent.fire(username);

È molto probabile che ci sia una classe che rappresenta un utente nella tua applicazione. Chiamiamo questa classe User.

Creare un bean CDI per gestire l'evento di autenticazione, trovare Userun'istanza con il nome utente corrispondente e assegnarlo al authenticatedUsercampo del produttore:

@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
    }
}

Il authenticatedUsercampo produce Userun'istanza che può essere iniettata in bean gestiti dal contenitore, come servizi JAX-RS, bean CDI, servlet ed EJB. Utilizzare il seguente pezzo di codice per iniettare Userun'istanza (in realtà, è un proxy CDI):

@Inject
@AuthenticatedUser
User authenticatedUser;

Notare che l' @Producesannotazione CDI è diversa dall'annotazione JAX-RS @Produces:

Assicurati di utilizzare l' @Producesannotazione CDI nel tuo AuthenticatedUserProducerbean.

La chiave qui è il bean annotato con @RequestScoped, che consente di condividere dati tra filtri e bean. Se non si desidera utilizzare gli eventi, è possibile modificare il filtro per archiviare l'utente autenticato in un bean con ambito richiesta e quindi leggerlo dalle classi di risorse JAX-RS.

Rispetto all'approccio che ha la precedenza su SecurityContext, l'approccio CDI consente di ottenere l'utente autenticato da bean diversi dalle risorse e dai provider JAX-RS.

Supportare l'autorizzazione basata sui ruoli

Per ulteriori informazioni su come supportare l'autorizzazione basata sul ruolo, consultare la mia altra risposta .

Token di emissione

Un token può essere:

  • Opaco: non rivela dettagli diversi dal valore stesso (come una stringa casuale)
  • Autosufficiente: contiene dettagli sul token stesso (come JWT).

Vedi i dettagli di seguito:

Stringa casuale come token

Un token può essere emesso generando una stringa casuale e conservandola in un database insieme all'identificatore utente e alla data di scadenza. Un buon esempio di come generare una stringa casuale in Java può essere visto qui . Puoi anche usare:

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

JWT (token Web JSON)

JWT (JSON Web Token) è un metodo standard per rappresentare i reclami in modo sicuro tra due parti ed è definito dalla RFC 7519 .

È un token autonomo e ti consente di memorizzare i dettagli nelle attestazioni . Queste attestazioni sono archiviate nel payload del token che è un codice JSON codificato come Base64 . Ecco alcuni reclami registrati nell'RFC 7519 e il loro significato (leggi l'RFC completo per ulteriori dettagli):

  • iss: Principale che ha emesso il token.
  • sub: Preside che è l'oggetto del JWT.
  • exp: Data di scadenza del token.
  • nbf: Ora in cui il token inizierà ad essere accettato per l'elaborazione.
  • iat: Ora in cui è stato emesso il token.
  • jti: Identificatore univoco per il token.

Tenere presente che non è necessario memorizzare nel token dati sensibili, come password.

Il payload può essere letto dal client e l'integrità del token può essere facilmente verificata verificandone la firma sul server. La firma è ciò che impedisce al token di essere manomesso.

Non sarà necessario persistere i token JWT se non è necessario seguirli. Tuttavia, persistendo nei token, avrai la possibilità di invalidare e revocare l'accesso ad essi. Per tenere traccia dei token JWT, invece di mantenere l'intero token sul server, è possibile mantenere l'identificatore token ( jtireclamo) insieme ad alcuni altri dettagli come l'utente per cui è stato emesso il token, la data di scadenza, ecc.

Quando i token persistono, prendere sempre in considerazione la rimozione di quelli vecchi per impedire la crescita indefinita del database.

Utilizzando JWT

Esistono alcune librerie Java per emettere e validare token JWT come:

Per trovare altre fantastiche risorse per lavorare con JWT, dai un'occhiata a http://jwt.io .

Gestione della revoca di token con JWT

Se si desidera revocare i token, è necessario tenerne traccia. Non è necessario memorizzare l'intero token sul lato server, memorizzare solo l'identificatore del token (che deve essere univoco) e alcuni metadati se necessario. Per l'identificatore token è possibile utilizzare UUID .

Il jtireclamo deve essere utilizzato per memorizzare l'identificatore token sul token. Quando si convalida il token, assicurarsi che non sia stato revocato verificando il valore del jtireclamo rispetto agli identificatori di token presenti sul lato server.

Per motivi di sicurezza, revoca tutti i token per un utente quando cambiano la password.

Informazioni aggiuntive

  • Non importa quale tipo di autenticazione decidi di utilizzare. Fallo sempre sulla cima di una connessione HTTPS per prevenire l' attacco man-in-the-middle .
  • Dai un'occhiata a questa domanda di Sicurezza delle informazioni per ulteriori informazioni sui token.
  • In questo articolo troverai alcune informazioni utili sull'autenticazione basata su token.

The server stores the previously generated token in some storage along with the user identifier and an expiration date. The server sends the generated token to the client. Com'è questo RESTful?
scottysseus

3
L'autenticazione basata su token @scottyseus funziona in base al modo in cui il server ricorda il token emesso. È possibile utilizzare i token JWT per l'autenticazione senza stato.
Cassiomolin

Che dire dell'invio di password con hash invece di plain (hash con nonce generato dal server)? Aumenta il livello di sicurezza (ad esempio quando non si utilizza https)? Nel caso di un uomo nel mezzo - sarà in grado di dirottare una sessione, ma almeno non otterrà la password
Denis Itskovich,

15
Non posso credere che questo non sia nella documentazione ufficiale.
Daniel M.

2
@grep In REST non esiste una sessione sul lato server. Di conseguenza, lo stato della sessione viene gestito sul lato client.
cassiomolina,

98

Questa risposta è tutto di autorizzazione ed è un complemento di mia risposta precedente circa l'autenticazione

Perché un'altra risposta? Ho tentato di espandere la mia risposta precedente aggiungendo dettagli su come supportare le annotazioni JSR-250. Tuttavia, la risposta originale è diventata troppo lunga e ha superato la lunghezza massima di 30.000 caratteri . Quindi ho spostato tutti i dettagli dell'autorizzazione su questa risposta, mantenendo l'altra risposta focalizzata sull'esecuzione dell'autenticazione e sull'emissione di token.


Supporto dell'autorizzazione basata sui ruoli con l' @Securedannotazione

Oltre al flusso di autenticazione mostrato nell'altra risposta , l'autorizzazione basata sul ruolo può essere supportata negli endpoint REST.

Crea un elenco e definisci i ruoli in base alle tue esigenze:

public enum Role {
    ROLE_1,
    ROLE_2,
    ROLE_3
}

Modifica l' @Securedannotazione di associazione dei nomi creata in precedenza per supportare i ruoli:

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

Quindi annota le classi di risorse e i metodi con cui @Securedeseguire l'autorizzazione. Le annotazioni del metodo sovrascriveranno le annotazioni della 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
        ...
    }
}

Creare un filtro con la AUTHORIZATIONpriorità, che viene eseguito dopo il AUTHENTICATIONfiltro di priorità definito in precedenza.

È ResourceInfopossibile utilizzare per ottenere la risorsa Methode la risorsa Classche gestiranno la richiesta e quindi estrarre le @Securedannotazioni da esse:

@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
    }
}

Se l'utente non dispone dell'autorizzazione per eseguire l'operazione, la richiesta viene interrotta con un 403(proibito).

Per conoscere l'utente che sta eseguendo la richiesta, vedere la mia risposta precedente . È possibile ottenerlo dal SecurityContext(che dovrebbe essere già impostato in ContainerRequestContext) o iniettarlo tramite CDI, a seconda dell'approccio scelto .

Se @Securedun'annotazione non ha ruoli dichiarati, si può presumere che tutti gli utenti autenticati possano accedere a quell'endpoint, ignorando i ruoli che hanno gli utenti.

Supporto dell'autorizzazione basata sui ruoli con annotazioni JSR-250

In alternativa alla definizione dei ruoli nell'annotazione @Securedcome mostrato sopra, è possibile prendere in considerazione le annotazioni JSR-250 come @RolesAllowed, @PermitAlle @DenyAll.

JAX-RS non supporta tali annotazioni immediatamente, ma potrebbe essere ottenuto con un filtro. Ecco alcune considerazioni da tenere a mente se si desidera supportarle tutte:

Quindi un filtro di autorizzazione che controlla le annotazioni JSR-250 potrebbe essere come:

@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.");
    }
}

Nota: l'implementazione di cui sopra si basa sulla maglia RolesAllowedDynamicFeature. Se usi Jersey, non è necessario scrivere il tuo filtro, basta usare l'implementazione esistente.


Esiste un repository github con questa elegante soluzione disponibile?
Daniel Ferreira Castro,

6
@DanielFerreiraCastro Certo. Dai un'occhiata qui .
Cassiomolin

Esiste un buon modo per convalidare che una richiesta provenga da un utente autorizzato E che l'utente possa modificare i dati perché "possiede" i dati (ad esempio, quindi un hacker non può usare il suo token per cambiare il nome di un altro utente)? So di poter verificare in ogni endpoint se user_id== token.userId, o qualcosa del genere, ma questo è molto ripetitivo.
mFeinstein,

@mFeinstein Una risposta per questo richiederebbe sicuramente più caratteri di quanti ne possa scrivere qui nei commenti. Solo per darti qualche direzione, potresti cercare la sicurezza a livello di riga .
cassiomolin

Riesco a vedere molti argomenti sui database per quando cerco la sicurezza a livello di riga, aprirò questa come una nuova domanda allora
mFeinstein
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.