我正在尝试在我的Dropwizard应用程序中编写一个安全资源的测试(我正在使用Dropwizard 0.8.1)。我运行应用程序时授权正常,但我似乎无法编写一个可行的测试。该代码主要基于示例Hello World应用程序,尽管传递了正确的授权标头,但我仍然获得了401。我有以下内容:
HelloWorldResource
package com.example.helloworld.resources;
import com.example.helloworld.core.Saying;
import com.example.helloworld.core.User;
import com.google.common.base.Optional;
import com.codahale.metrics.annotation.Timed;
import io.dropwizard.auth.Auth;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.concurrent.atomic.AtomicLong;
@Path("/hello-world")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {
private final String template;
private final String defaultName;
private final AtomicLong counter;
public HelloWorldResource(String template, String defaultName) {
this.template = template;
this.defaultName = defaultName;
this.counter = new AtomicLong();
}
@GET
@Timed
public Saying sayHello(@QueryParam("name") Optional<String> name) {
final String value = String.format(template, name.or(defaultName));
return new Saying(counter.incrementAndGet(), value);
}
@GET
@Path("secure")
@Timed
public Saying sayHelloSecurely(@Auth User user) {
final String value = String.format(template, user.getName());
return new Saying(counter.incrementAndGet(), value);
}
}
说
package com.example.helloworld.core;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.Length;
public class Saying {
private final long id;
@Length(max = 3)
private final String content;
@JsonCreator
public Saying(@JsonProperty long id, @JsonProperty String content) {
this.id = id;
this.content = content;
}
@JsonProperty
public long getId() {
return id;
}
@JsonProperty
public String getContent() {
return content;
}
}
身份验证
package com.example.helloworld.auth;
import com.example.helloworld.core.User;
import com.google.common.base.Optional;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class SimpleAuthenticator implements Authenticator<BasicCredentials, User> {
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
if ("secret".equals(credentials.getPassword())) {
return Optional.of(new User(credentials.getUsername()));
}
return Optional.absent();
}
}
SecureTest
package com.example.helloworld.resources;
import com.example.helloworld.auth.SimpleAuthenticator;
import com.example.helloworld.core.Saying;
import com.example.helloworld.core.User;
import io.dropwizard.auth.AuthFactory;
import io.dropwizard.auth.basic.BasicAuthFactory;
import io.dropwizard.testing.junit.ResourceTestRule;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.ClassRule;
import org.junit.Test;
import javax.ws.rs.core.HttpHeaders;
public class SecureHelloWorldResourceTest {
@ClassRule
public static final ResourceTestRule resources =
ResourceTestRule.builder()
.addProvider(RolesAllowedDynamicFeature.class)
.addProvider(AuthFactory.binder(new BasicAuthFactory<User>(new SimpleAuthenticator(),
"Authentication Realm",
User.class)))
.addResource(new HelloWorldResource("Hey There %s", "DefaultName"))
.build();
@Test
public void testGetPerson() {
resources
.client()
.target("/hello-world/secure")
.request()
.header(HttpHeaders.AUTHORIZATION, "Basic Z29vZC1ndXk6c2VjcmV0").get(Saying.class); //Decodes to "good-guy:secret"
}
}
更新
阅读https://github.com/dropwizard/dropwizard/issues/922后,我开始使用Grizzly容器,现在我收到了与Saying序列化有关的新错误:
堆栈跟踪
javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:868)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:785)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:790)
at org.glassfish.jersey.client.JerseyInvocation.access$500(JerseyInvocation.java:91)
at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:687)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:683)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:411)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:307)
at com.example.helloworld.resources.SecureHelloWorldResourceTest.testGetPerson(SecureHelloWorldResourceTest.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at io.dropwizard.testing.junit.ResourceTestRule$1.evaluate(ResourceTestRule.java:199)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Argument #0 of constructor [constructor for com.example.helloworld.core.Saying, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:267)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:242)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:143)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:439)
at com.fasterxml.jackson.databind.ObjectReader._findRootDeserializer(ObjectReader.java:1564)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1403)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:858)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:808)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:266)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:236)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1085)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:853)
... 41 more
Caused by: java.lang.IllegalArgumentException: Argument #0 of constructor [constructor for com.example.helloworld.core.Saying, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addDeserializerConstructors(BasicDeserializerFactory.java:508)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:325)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:258)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:216)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:143)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:405)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:354)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:262)
... 53 more
Process finished with exit code -1
安全测试
package com.example.helloworld.resources;
import com.example.helloworld.auth.SimpleAuthenticator;
import com.example.helloworld.core.Saying;
import com.example.helloworld.core.User;
import io.dropwizard.auth.AuthFactory;
import io.dropwizard.auth.basic.BasicAuthFactory;
import io.dropwizard.testing.junit.ResourceTestRule;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.ClassRule;
import org.junit.Test;
import javax.ws.rs.core.HttpHeaders;
public class SecureHelloWorldResourceTest {
@ClassRule
public static final ResourceTestRule resources =
ResourceTestRule.builder()
.addProvider(RolesAllowedDynamicFeature.class)
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(AuthFactory.binder(new BasicAuthFactory<User>(new SimpleAuthenticator(),
"Authentication Realm",
User.class)))
.addResource(new HelloWorldResource("Hey There %s", "DefaultName"))
.build();
@Test
public void testGetPerson() {
resources
.getJerseyTest()
//.client()
.target("/hello-world/secure")
.request()
.header(HttpHeaders.AUTHORIZATION, "Basic Z29vZC1ndXk6c2VjcmV0").get(Saying.class);
}
}
答案 0 :(得分:1)
我通过使用命名为JsonProperties的构造函数的params注释来解决更新中的序列化错误,这似乎有点多余。
public class Saying {
private final long id;
@Length(max = 3)
private final String content;
@JsonCreator
public Saying(@JsonProperty("id") long id, @JsonProperty("content") String content) {//Note the addition of the ("id") and ("content")
this.id = id;
this.content = content;
}
@JsonProperty
public long getId() {
return id;
}
@JsonProperty
public String getContent() {
return content;
}
}
现在测试看起来像这样:
package com.example.helloworld.resources;
import com.example.helloworld.auth.SimpleAuthenticator;
import com.example.helloworld.core.Saying;
import com.example.helloworld.core.User;
import io.dropwizard.auth.AuthFactory;
import io.dropwizard.auth.basic.BasicAuthFactory;
import io.dropwizard.testing.junit.ResourceTestRule;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.core.HttpHeaders;
public class SecureHelloWorldResourceTest {
@Rule
public ExpectedException exception = ExpectedException.none();
@ClassRule
public static final ResourceTestRule resources =
ResourceTestRule.builder()
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(AuthFactory.binder(new BasicAuthFactory<User>(new SimpleAuthenticator(),
"Authentication Realm",
User.class)))
.addResource(new HelloWorldResource("Hey There %s", "DefaultName"))
.build();
@Test
public void testAuthorizedRequest() {
Saying result = resources
.getJerseyTest()
.target("/hello-world/secure")
.request()
.header(HttpHeaders.AUTHORIZATION, "Basic Z29vZC1ndXk6c2VjcmV0").get(Saying.class);//good-guy:secret
Assert.assertEquals("Hey There good-guy", result.getContent());
}
@Test
public void testDeniedRequest() {
exception.expect(NotAuthorizedException.class);
resources
.getJerseyTest()
.target("/hello-world/secure")
.request()
.header(HttpHeaders.AUTHORIZATION, "Basic YmFkLWd1eTpwYXNzd29yZA==").get(Saying.class);//bad-guy:password
}
}