2015-04-21 21 views
9

Ho un servizio JAX-RS in cui desidero che tutti i miei utenti accedano ai miei servizi, ma solo quelli che hanno i diritti per vedere il risultato. La sicurezza basata sui ruoli e i metodi REALMS e di autenticazione già esistenti non si adattano al mio requisito.Autorizzazione JAX-RS personalizzata - utilizzo del JWT in ogni richiesta

Ad esempio:

  1. utente viene autenticato contro uno servizio REST e lo mando JWT token con il suo ID
  2. utente chiede altra risorsa e invia la sua JWT con il suo ID a ogni richiesta
  3. I controlla il suo id utente (da JWT) e se la logica aziendale restituisce il risultato li rimando, altrimenti invio set di risultati vuoto o stato HTTP specifico

La domanda è: dove devo controllare ID utente, in qualche filtro separato, contesto di sicurezza o in ogni implementazione del metodo REST? Come fornire i metodi REST con questo ID, securityContext può essere iniettato in ogni metodo dopo aver filtrato la richiesta per ID?

Sto utilizzando GlassFish 4.1 e l'implementazione Jersey JAX-RS.

risposta

23

È possibile eseguire questa logica in un ContainerRequestFilter. È piuttosto comune gestire le funzionalità di sicurezza personalizzate qui.

Alcune cose da considerare

  1. La classe deve essere annotato con @Priority(Priorities.AUTHENTICATION) quindi viene eseguita prima di altri filtri, se presenti.

  2. Si consiglia di utilizzare SecurityContext all'interno del filtro. Quello che faccio è implementare un SecurityContext. Puoi davvero implementarlo in ogni caso.

Ecco un semplice esempio, senza alcuna delle logiche di sicurezza

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

    @Override 
    public void filter(ContainerRequestContext requestContext) throws IOException { 
     SecurityContext originalContext = requestContext.getSecurityContext(); 
     Set<String> roles = new HashSet<>(); 
     roles.add("ADMIN"); 
     Authorizer authorizer = new Authorizer(roles, "admin", 
               originalContext.isSecure()); 
     requestContext.setSecurityContext(authorizer); 
    } 

    public static class Authorizer implements SecurityContext { 

     Set<String> roles; 
     String username; 
     boolean isSecure; 
     public Authorizer(Set<String> roles, final String username, 
              boolean isSecure) { 
      this.roles = roles; 
      this.username = username; 
      this.isSecure = isSecure; 
     } 

     @Override 
     public Principal getUserPrincipal() { 
      return new User(username); 
     } 

     @Override 
     public boolean isUserInRole(String role) { 
      return roles.contains(role); 
     } 

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

     @Override 
     public String getAuthenticationScheme() { 
      return "Your Scheme"; 
     } 
    } 

    public static class User implements Principal { 
     String name; 

     public User(String name) { 
      this.name = name; 
     } 

     @Override 
     public String getName() { return name; } 
    } 
} 

Alcune cose da notare

  • Ho creato un SecurityContext
  • ho aggiunto alcuni ruoli e li ha usati per il metodo isUserInRole. Questo sarà usato per l'autorizzazione.
  • Ho creato una classe personalizzata User, che implementa java.security.Principal. Sono tornato oggetto questa usanza
  • Infine ho impostato la nuova SecurityContext nel ContainerRequestContext

Ora che cosa? Diamo un'occhiata a una semplice classe di risorse

@Path("secure") 
public class SecuredResource { 
    @GET 
    @RolesAllowed({"ADMIN"}) 
    public String getUsername(@Context SecurityContext securityContext) { 
     User user = (User)securityContext.getUserPrincipal(); 
     return user.getName(); 
    } 
} 

Alcune cose da notare:

  • SecurityContext viene iniettato nel metodo.
  • Otteniamo il Principal e lo trasmettiamo a User.Quindi è davvero possibile creare qualsiasi classe che implementa Principal e utilizzare questo oggetto come si desidera.
  • L'uso dell'annotazione @RolesAllowed. Con Jersey, c'è un filtro che controlla lo SecurityContext.isUserInRole passando in ciascun valore nell'annotazione @RolesAllowed per vedere se l'Utente è autorizzato ad accedere alla risorsa.

    Per abilitare questa funzionalità con Jersey, abbiamo bisogno di registrare il RolesAllowedDynamicFeature

    @ApplicationPath("/api") 
    public class AppConfig extends ResourceConfig { 
    
        public AppConfig() { 
         packages("packages.to.scan"); 
         register(RolesAllowedDynamicFeature.class); 
        } 
    } 
    
+0

grazie per la risposta. Ho pensato di poter utilizzare ContainerRequestFilter, ma non ho bisogno di ruoli e SecurityContext per il mio requisito. E 'troppo semplice usare solo il filtro servlet, e per ogni richiesta alle risorse REST farei la desmissioning dell'intestazione JWT inviata in precedenza, estrarre le informazioni utente da esso e passarle ai metodi di servizio per determinare i livelli di concessione dell'utente all'interno di ogni mehod? Sembra una semplice soluzione pragmatica che dovrebbe funzionare ... – D00de

+0

Praticamente quello che puoi fare in un filtro servlet, puoi farlo nel ContainerRequestFilter. Ma con quest'ultimo, hai accesso all'applicazione Jersey –

+0

grazie, ho implementato la tua soluzione iniettando dati JWT su User principal e poi recuperato da SecurityContext inject in ogni metodo REST che ho. Ho il mio voto e l'accettazione per il tuo aiuto, signore. – D00de

1

ero alla ricerca di una soluzione che è Jersey indipendente e lavora per wildfly -> ha trovato questo esempio di implementazione GitHub:

https://github.com/sixturtle/examples/tree/master/jaxrs-jwt-filter

dovrebbe dare un suggerimento come risolverlo pulito.

implementare un JWTRequestFilter che implementa ContainerRequestFilter https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java

come detto in precedenza e registrare il filtro come fornitore RESTEasy in web.xml:

<context-param> 
     <description>Custom JAX-RS Providers</description> 
     <param-name>resteasy.providers</param-name> 
     <param-value>com.sixturtle.jwt.JWTRequestFilter</param-value> 
</context-param> 
<context-param> 
     <param-name>resteasy.role.based.security</param-name> 
     <param-value>true</param-value> 
</context-param>