2014-12-03 9 views
5

Конечные точки с Jersey.Как издеваться над SecurityContext

Я хочу, чтобы обеспечить конечную точку с ContainerRequestFilter

@Provider 
@Secured 
public class AuthorizationRequestFilter implements ContainerRequestFilter { 

    @Override 
    public void filter(ContainerRequestContext requestContext) throws IOException { 
     final SecurityContext securityContext = 
       requestContext.getSecurityContext(); 

     //TODO: on logger here... 
     System.out.printf("Filtering %s request... AuthorizationRequestFilter\n", requestContext.getMethod()); 
     requestContext.getHeaders().add("X-Secured-By", "Jersey >_<"); 
     System.out.printf("SecurityContext: %s (%s).\n", securityContext, securityContext.getAuthenticationScheme()); 

     if (securityContext == null || !securityContext.isUserInRole("privileged")) { 
      requestContext.abortWith(new UnauthorizedResponse().getResponse()); 
     } 
    } 
} 

аннотацию @Secured:

@NameBinding 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Secured {} 

Так что я могу сделать это:

@Path("foobar") 
public class FooResource { 

    //... 

    @Context 
    SecurityContext securityContext; 

    //... 

    @GET 
    @Secured 
    @Path(value = "foo") 
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getFoo(@Context SecurityContext sc, @Context UriInfo ui, @Context HttpHeaders hh) { 
     // ... 
    } 

    //... 

И я это делаю право (я думаю), потому что с моим тестом я даже не прохожу через getFoo конечная точка, но это ContainerRequestFilter, который отталкивает меня. На самом деле я получаю это («X-Обеспеченные-By» заголовок ручной работы):

Headers: {X-Secured-By=[Jersey >_< kicked you out!], Content-Length=[97], Date=[Wed, 03 Dec 2014 17:46:50 GMT], Content-Type=[application/json], X-Powered-By=[Jersey ^_^]} 
Response: InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9998/urler/test, status=401, reason=Unauthorized}} 

Теперь было бы неплохо, чтобы дразнить SecurityContext. Это то, что я делаю ... и если я здесь, это очевидно глупо и/или неправильно.

public class UrlerResourceTest extends JerseyTest { 
    //.... 

    @Override 
    public TestContainerFactory getTestContainerFactory() { 
     GrizzlyTestContainerFactory grizzlyTestContainerFactory = new GrizzlyTestContainerFactory(); 
     System.out.printf("The GrizzlyTestContainerFactory: %s ", grizzlyTestContainerFactory); 
     // just for debugging... 
     return grizzlyTestContainerFactory; 
    } 

    @Test 
    public void testSecuredEndpoint() throws JSONException { 

     SecurityContext securityContext = Mockito.mock(SecurityContext.class); 
     Mockito.when(securityContext.isUserInRole(anyString())).thenReturn(true); 
     Mockito.when(securityContext.getAuthenticationScheme()).thenReturn("Just Mocking..."); 
     ReflectionTestUtils.setField(resource, "securityContext", securityContext, SecurityContext.class); 

     final Response response = target("foobar") 
      .path("foo") 
      .request(MediaType.APPLICATION_JSON) 
      .get(); 
     System.out.println(getFormattedStringResponseInfo(response)); 

     JSONObject entity = new JSONObject(response.readEntity(String.class)); 
     assertTrue(entity.get("secured").equals(true)); 
     assertTrue(response.getHeaders().containsKey("X-Secured-By")); 
     assertEquals(Status.OK.getStatusCode(), response.getStatus()); 
    } 

Как я могу издеваться над SecurityContext в моих тестах?

Большое вам спасибо.

ответ

7

Отказ от ответственности: Я на самом деле не пользователь Mockito, но от того, что я понимаю, насмешливый используются в ситуации, когда вы инжектированные зависимости классов (поля), и вы дразнить эти зависимости. В этом случае вам все равно нужно установить поле с издеваемым объектом. Например

public class TestClass { 
    TestService testService; 
    public void doTest() { 
     System.out.println(testService.getString()); 
    } 
    public void setTestService(TestService testService) { 
     this.testService = testService; 
    } 
} 
public class TestService { 
    public String getString() { 
     return "Hello world"; 
    } 
} 
@Test 
public void toTest() { 
    TestService testService = Mockito.mock(TestService.class); 
    Mockito.when(testService.getString()).thenReturn("Hello Squirrel"); 
    TestClass testClass = new TestClass(); 
    testClass.setTestService(testService); 
    testClass.doTest(); 
} 

Вы можете увидеть, мы устанавливаем на TestService в TestClass с издевались объекта. Это не самый лучший пример, поскольку мы могли бы просто создать экземпляр TestService, но он показывает, насколько я понимаю, как должно насмехаться.

Как я уже сказал, я не вижу, как это можно сделать с помощью AuthorizationRequestFilter, так как он обрабатывается тестовым контейнером, и мы не создаем его для модульного теста. Даже если бы мы были, казалось бы, навязчивым (и избыточным) добавить поле SecurityContext.

Таким образом, без полного теста интеграции, когда мы запускаем сервер, и используя возможности аутентификации сервера, будет трудно обрабатывать SecurityContext в этом случае, так как SecurityContext создается контейнером, принимая информацию от механизм проверки подлинности контейнеров с сервлетами.

Один из способов достичь этого, хотя (который ИМО, кажется, не очень элегантно - но работает), без проверки полной интеграции, чтобы создать аа фильтр, который выполняет перед тем ваш AuthorizationRequestFilter и установите SecurityContext от есть. Тестирование в сторону, это на самом деле довольно распространено в тех случаях, когда нам нужно реализовать собственный механизм аутентификации.

Пример того, как вы могли бы сделать это для модульного тестирования, может быть что-то вроде:

public class UrlerResourceTest extends JerseyTest { 
    ... 
    @Override 
    public Application configure() { 
     return new ResourceConfig(FooResource.class) 
       .register(AuthorizationRequestFilter.class) 
       .register(AuthenticationFilter.class); 
    } 

    @Provider 
    @Priority(Priorities.AUTHENTICATION) 
    public static class AuthenticationFilter implements ContainerRequestFilter { 
     @Override 
     public void filter(ContainerRequestContext requestContext) throws IOException { 
      requestContext.setSecurityContext(new SecurityContext() { 
       @Override 
       public Principal getUserPrincipal() { 
        return new Principal() { 
         @Override 
         public String getName() { 
          return "Stackoverflow"; 
         } 
        }; 
       } 
       @Override 
       public boolean isUserInRole(String string) { 
        return "privileged".equals(string); 
       } 
       @Override 
       public boolean isSecure() { return true; } 
       @Override 
       public String getAuthenticationScheme() { return "BASIC"; }     
      }); 
     } 
    } 
    ... 
} 

Этот фильтр будет выполнять до AuthorizationRequestFilter из-за @Priority аннотацию. Мы установили его в Priorities.AUTHENTICATION, который будет перед любым другим фильтром без такой аннотации. (См Priorities API и Priorities with Jersey. Также SecurityContext будут переданы между фильтрами, а также вводить в свой класс ресурсов.

Как я уже сказал, я не думаю, что это очень элегантно, чтобы создать еще один фильтр, но работает с этой целью. Также я не слишком хорошо знаком с тестовой платформой Jersey, поскольку я все еще начинаю с нее, но есть много вариантов конфигурации для развертывания в контексте сервлета. Я не знаю, можем ли мы сконфигурировать необходимые механизм аутентификации для этого случая, но это может быть что-то стоит смотреть в


Edit:. В начале я объяснил, как установить поле для тестового объекта, но мы также можем передать обманутый объект методу. Например, мы могли бы издеваться над ContainterRequestContext в методе filter и сами звонить filter, передавая издевательство ContainerRequestContext. Но это полезно только тогда, когда мы на самом деле единично тестируем класс фильтра и сами создаем его, что здесь не так.

+1

Спасибо @peeskillet ... и с вашей записью об издевательстве «ContainterRequestContext» вы дали мне хорошую идею ... – zeroed

+0

@peeskillet, хочу поцеловать вас. Везде, где я ищу решения, связанные с Джерси, я отвечаю на ваши полезные ответы! Благодаря! – erwineberhard

+0

@erwineberhard Мне, возможно, придется начать зарядку в ближайшее время :-) –

Смежные вопросы