2017-01-17 1 views
2

Ich möchte Jersey DeclarativeLinking Feature mit seiner Funktion RolesAllowed kombinieren.Trusted deklarative Verknüpfung Berechtigungen mit RolesAllowed

Ich bin in der Lage, Links erfolgreich in eine Antwort zu injizieren, aber diese Injektion beachtet die RolesAllowed-Annotation dieses Endpunkts nicht.

Stellen Sie sich als Beispiel zwei Benutzer vor. Nennen Sie sie Admin und Benutzer. Beide können auf GET-Ressourcen zugreifen, aber nur Administratoren können auf DELETE zugreifen.

Wenn eine Ressourcenmethode durch @RolesAllowed geschützt ist, würde ich erwarten, dass ein Benutzer, der diese Rolle nicht hat, diesen Endpunkt nicht injiziert bekommt. Das scheint nicht der Fall zu sein.

Ich habe das Beispiel gefunden gefunden here.

Im Folgenden finden Sie eine vereinfachte Version des Projekts, an dem ich zur Veranschaulichung arbeite.

Für Admin würde ich folgende json erwarten,

{ 
    "id" : 1 
    "value" : "someValue", 
    "links" : [ 
     { 
      "href": "http://localhost/context/model/1", 
      "rel": "self", 
      "type": "GET" 
     }, 
     { 
      "href": "http://localhost/context/model/1", 
      "rel": "delete", 
      "type": "DELETE" 
     }, 
    }, 
} 

Das ist genau das, was ich bekommen. Leider bekomme ich diese Antwort auch für Benutzer, die nicht die richtigen Rollen haben, um auf diesen Endpunkt zuzugreifen. Der Benutzer wird nach wie vor mit einem Link zu dem Lösch Endpunkt zur Verfügung gestellt werden, aber aufgrund RolesAllowed, dass Benutzer erhalten ein 403.

Modell

public class Model { 

private int id; 
private String value; 
//constructor/getters/setters 

Modell Darstellung

public class ModelRep { 
    @XmlPath(".") //Allows me to flatten json so that links and document are at the same level 
    private Model model; 

    @InjectLinks({ 
     @InjectLink(
      rel = "self", 
      type = "GET", 
      resource = ModelResource.class, 
      method = "getModel", 
      bindings = @Binding(name = "id", value = "${instance.id}"), 
      style = Style.ABSOLUTE 
     ), 
     @InjectLink(
      rel = "delete", 
      type = "DELETE", 
      resource = ModelResource.class, 
      method = "deleteModel", 
      bindings = @Binding(name = "id", value = "${instance.id}"), 
      style = Style.ABSOLUTE 
     ) 
    }) 
    @XmlJavaTypeAdapter(LinkAdapter.class) 
    private List<Link> links; 
    //constructor/getters/setters 

Modellressource

@Path("/model") 
@Produces(MediaType.APPLICATION_JSON) 
public class ModelResource { 

    @GET 
    @Path("/{id}") 
    public Response getModel(@PathParam("id") int id) { 
     Model m = dao.get(id); 
     ModelRep mr = new modelRep(m); 
     GenericEntity<ModelRep> ent = new GenericEntity<ModelRep>(mr) {}; 
     return Response.status(Status.OK).entity(ent); 
    } 

    @DELETE 
    @Path("/{id}") 
    @RolesAllowed(Roles.ADMIN) 
    public Response deleteModel(@PathParam("id") int id) { 
     dao.delete(id); 
     return Response.status(Status.NO_CONTENT); 
    } 

Frage Gibt es eine Möglichkeit, dass bedingte deklarative Verknüpfung basierend auf RolesAllowed erreicht werden kann?

Ich weiß, dass eine Bedingungsklausel für die Annotation InjectLink existiert, aber die zulässigen Werte sind Instanz (ModelRep), Entity (Response-Objekt, denke ich?), Ressource (ModelResource).

Mir ist keine Möglichkeit bekannt, eine dieser Methoden zu verwenden, um nach Berechtigungen zu suchen.

schätzen irgendwelche Ratschläge

Antwort

1

Also habe ich eine Lösung zu finden, die am Ende ein wenig hacky ist aber ziemlich gut funktioniert.

Jede Ressource erstreckt sich eine Basisressourcen clase

@Path("/model") 
@Produces(MediaType.APPLICATION_JSON) 
public class ModelResource extends BaseResource { 

BaseResource eine öffentliche Methode definiert, ob oder ob nicht ein spezielles Verfahren für die aktuelle Ressource zurückgibt mit der RolesAllowed Annotation beschränkt.

public abstract class BaseResource { 

    @Context 
    private SecurityContext security; 

    public boolean isMethodRoleProtected(String sMethod) { 
     Method[] methods = this.getClass().getDeclaredMethods(); 
     for (Method m : methods) { 
      if (m.getName().equalsIgnoreCase(sMethod)) { 
       Annotation[] annotations = m.getAnnotations(); 
       for (Annotation a : annotations) { 
        if (a instanceof RolesAllowed) { 
         String[] roles = ((RolesAllowed) a).value; 
         return userHasRole(roles); 
        } 
       } 
      } 
     } 
     return false; 
    } 

    protected boolean userHasRole(String... roles) { 
     for (String role : roles) { 
      if (security.isUserInRole(role)) { 
       return true; 
      } 
     } 
     return false; 
    } 
} 

Schließlich ist die InjectLink Anmerkung wird so aussehen,

@InjectLink(
    rel = "self", 
    type = "GET", 
    resource = SomeResource.class, 
    method = "someMethod", 
    bindings = @Binding(name = "id", value = "${instance.id}"), 
    style = Style.ABSOLUTE, 
    condition = "${resource.isMethodRoleProtected(someMethod)}" 
), 

'Ressource' ist die Instanz der aktuellen Endpunkt-Klasse, die BaseResource erstreckt. Daher wird diese Verknüpfung nur dann eingefügt, wenn der aktuelle Benutzer eine Rolle hat, die in der Annotation RolesAllowed dieser Methode vorhanden ist.

Diese Lösung erfordert eine Implementierung von SecurityContext.

Wenn jemand eine bessere/sauberere Lösung oder eine Möglichkeit hat, die zu verbessern, die ich gefunden habe, würde ich immer noch gerne hören und werde die angenommene Antwort entsprechend anpassen. Prost