如何模拟SecurityContext
@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-担保,通过”报头是手工制作):
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
?
非常感谢你提前。
免责声明:我不是一个真正的用户的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
是由容器创建的,从中获取信息底层的servlet容器认证机制。你可以做到这一点,但(这IMO似乎并不很优雅 - 但作品)
的一种方式,没有一个完整的集成测试,是创造出一个过滤器执行你的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"; }
});
}
}
...
}
这个过滤器会因为@Priority
注释的AuthorizationRequestFilter
之前执行。我们将其设置为Priorities.AUTHENTICATION
,之前会在没有这种注释的任何其他过滤器之前。 (见Priorities API和Priorities with Jersey。另外,SecurityContext
将沿着过滤器之间传递,并注入到你的资源类。
正如我所说的,我不认为这是非常优雅,以创建另一个过滤器,但它我也不太熟悉Jersey测试框架,因为我仍然从头开始,但是在servlet上下文中有很多配置选项可供配置,我不知道我们是否可以配置需要的对于这种情况下的认证机制,但它可能是值得探讨
编辑:在开始时我解释了如何设置测试对象的字段,但我们也可以将模拟对象传递给一个方法。例如,我们可以嘲笑ContainterRequestContext
中的filter
方法,并且自己调用filter
,通过嘲讽的ContainerRequestContext
。但是,这只有在我们实际上单元测试过滤器类并实例化它时才有用,在这里不是这种情况。
谢谢@peeskillet ...和你在嘲笑'ContainterRequestContext'时给你一个不错的想法... – zeroed 2014-12-04 09:08:38
@peeskillet,想吻你。无论我在哪里搜索与泽西岛有关的解决方案,谢谢! – erwineberhard 2015-12-23 20:13:26
@erwineberhard我可能不得不马上开始收费:-) – 2015-12-24 02:36:42