gpt4 book ai didi

dropwizard - 如何使用 Dropwizard 测试 HMAC 身份验证?

转载 作者:行者123 更新时间:2023-12-04 17:02:41 25 4
gpt4 key购买 nike

我刚刚开始使用 Dropwizard 0.4.0,我想要一些有关 HMAC 身份验证的帮助。有人有什么建议吗?

先感谢您。

最佳答案

目前 Dropwizard 不支持开箱即用的 HMAC 身份验证,因此您必须编写自己的身份验证器。 HMAC 身份验证的典型选择是使用 HTTP 授权 header 。以下代码要求此 header 采用以下格式:

Authorization: <algorithm> <apiKey> <digest>

一个例子是
Authorization: HmacSHA1 abcd-efgh-1234 sdafkljlkansdaflk2354jlkj5345345dflkmsdf

摘要是在 URL 编码之前从正文(编码实体)的内容构建的,其中 HMAC 共享 key 附加为 base64。对于非正文请求,例如 GET 或 HEAD,内容被视为完整的 URI 路径和附加 key 的参数。

要以 Dropwizard 可以使用的方式实现这一点,您需要将 dropwizard-auth 模块中的 BasicAuthenticator 代码复制到您自己的代码中,并使用以下内容对其进行修改:
import com.google.common.base.Optional;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.yammer.dropwizard.auth.AuthenticationException;
import com.yammer.dropwizard.auth.Authenticator;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

class HmacAuthInjectable<T> extends AbstractHttpContextInjectable<T> {
private static final String PREFIX = "HmacSHA1";
private static final String HEADER_VALUE = PREFIX + " realm=\"%s\"";

private final Authenticator<HmacCredentials, T> authenticator;
private final String realm;
private final boolean required;

HmacAuthInjectable(Authenticator<HmacCredentials, T> authenticator, String realm, boolean required) {
this.authenticator = authenticator;
this.realm = realm;
this.required = required;
}

public Authenticator<HmacCredentials, T> getAuthenticator() {
return authenticator;
}

public String getRealm() {
return realm;
}

public boolean isRequired() {
return required;
}

@Override
public T getValue(HttpContext c) {

try {
final String header = c.getRequest().getHeaderValue(HttpHeaders.AUTHORIZATION);
if (header != null) {

final String[] authTokens = header.split(" ");

if (authTokens.length != 3) {
// Malformed
HmacAuthProvider.LOG.debug("Error decoding credentials (length is {})", authTokens.length);
throw new WebApplicationException(Response.Status.BAD_REQUEST);
}

final String algorithm = authTokens[0];
final String apiKey = authTokens[1];
final String signature = authTokens[2];
final String contents;

// Determine which part of the request will be used for the content
final String method = c.getRequest().getMethod().toUpperCase();
if ("GET".equals(method) ||
"HEAD".equals(method) ||
"DELETE".equals(method)) {
// No entity so use the URI
contents = c.getRequest().getRequestUri().toString();
} else {
// Potentially have an entity (even in OPTIONS) so use that
contents = c.getRequest().getEntity(String.class);
}

final HmacCredentials credentials = new HmacCredentials(algorithm, apiKey, signature, contents);

final Optional<T> result = authenticator.authenticate(credentials);
if (result.isPresent()) {
return result.get();
}
}
} catch (IllegalArgumentException e) {
HmacAuthProvider.LOG.debug(e, "Error decoding credentials");
} catch (AuthenticationException e) {
HmacAuthProvider.LOG.warn(e, "Error authenticating credentials");
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}

if (required) {
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.AUTHORIZATION,
String.format(HEADER_VALUE, realm))
.entity("Credentials are required to access this resource.")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}
return null;
}
}

以上内容并不完美,但可以帮助您入门。您可能需要引用 MultiBit Merchant release candidate source code(MIT 许可证)以获得更新的版本和各种支持类。

下一步是将身份验证过程集成到您的 ResourceTest 子类中。不幸的是,Dropwizard 在 v0.4.0 中没有为身份验证提供程序提供一个好的入口点,因此您可能需要引入自己的基类,类似于以下内容:
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.test.framework.AppDescriptor;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.LowLevelAppDescriptor;
import com.xeiam.xchange.utils.CryptoUtils;
import com.yammer.dropwizard.bundles.JavaBundle;
import com.yammer.dropwizard.jersey.DropwizardResourceConfig;
import com.yammer.dropwizard.jersey.JacksonMessageBodyProvider;
import com.yammer.dropwizard.json.Json;
import org.codehaus.jackson.map.Module;
import org.junit.After;
import org.junit.Before;
import org.multibit.mbm.auth.hmac.HmacAuthProvider;
import org.multibit.mbm.auth.hmac.HmacAuthenticator;
import org.multibit.mbm.persistence.dao.UserDao;
import org.multibit.mbm.persistence.dto.User;
import org.multibit.mbm.persistence.dto.UserBuilder;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Set;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
* A base test class for testing Dropwizard resources.
*/
public abstract class BaseResourceTest {
private final Set<Object> singletons = Sets.newHashSet();
private final Set<Object> providers = Sets.newHashSet();
private final List<Module> modules = Lists.newArrayList();

private JerseyTest test;

protected abstract void setUpResources() throws Exception;

protected void addResource(Object resource) {
singletons.add(resource);
}

public void addProvider(Object provider) {
providers.add(provider);
}

protected void addJacksonModule(Module module) {
modules.add(module);
}

protected Json getJson() {
return new Json();
}

protected Client client() {
return test.client();
}

@Before
public void setUpJersey() throws Exception {
setUpResources();
this.test = new JerseyTest() {
@Override
protected AppDescriptor configure() {
final DropwizardResourceConfig config = new DropwizardResourceConfig();
for (Object provider : JavaBundle.DEFAULT_PROVIDERS) { // sorry, Scala folks
config.getSingletons().add(provider);
}
for (Object provider : providers) {
config.getSingletons().add(provider);
}
Json json = getJson();
for (Module module : modules) {
json.registerModule(module);
}
config.getSingletons().add(new JacksonMessageBodyProvider(json));
config.getSingletons().addAll(singletons);
return new LowLevelAppDescriptor.Builder(config).build();
}
};
test.setUp();
}

@After
public void tearDownJersey() throws Exception {
if (test != null) {
test.tearDown();
}
}

/**
* @param contents The content to sign with the default HMAC process (POST body, GET resource path)
* @return
*/
protected String buildHmacAuthorization(String contents, String apiKey, String secretKey) throws UnsupportedEncodingException, GeneralSecurityException {
return String.format("HmacSHA1 %s %s",apiKey, CryptoUtils.computeSignature("HmacSHA1", contents, secretKey));
}

protected void setUpAuthenticator() {
User user = UserBuilder
.getInstance()
.setUUID("abc123")
.setSecretKey("def456")
.build();

//
UserDao userDao = mock(UserDao.class);
when(userDao.getUserByUUID("abc123")).thenReturn(user);

HmacAuthenticator authenticator = new HmacAuthenticator();
authenticator.setUserDao(userDao);

addProvider(new HmacAuthProvider<User>(authenticator, "REST"));
}
}

同样,上面的代码并不完美,但其想法是允许模拟的 UserDao 为标准用户提供已知的共享 key 。出于测试目的,您必须引入自己的 UserBuilder 实现。

最后,使用上面的代码,一个 Dropwizard 资源具有这样的端点:
import com.google.common.base.Optional;
import com.yammer.dropwizard.auth.Auth;
import com.yammer.metrics.annotation.Timed;
import org.multibit.mbm.core.Saying;
import org.multibit.mbm.persistence.dto.User;

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("/")
@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
@Path("/hello-world")
public Saying sayHello(@QueryParam("name") Optional<String> name) {
return new Saying(counter.incrementAndGet(),
String.format(template, name.or(defaultName)));
}

@GET
@Timed
@Path("/secret")
public Saying saySecuredHello(@Auth User user) {
return new Saying(counter.incrementAndGet(),
"You cracked the code!");
}

}

可以使用如下配置的单元测试进行测试:
import org.junit.Test;
import org.multibit.mbm.core.Saying;
import org.multibit.mbm.test.BaseResourceTest;

import javax.ws.rs.core.HttpHeaders;

import static org.junit.Assert.assertEquals;

public class HelloWorldResourceTest extends BaseResourceTest {


@Override
protected void setUpResources() {
addResource(new HelloWorldResource("Hello, %s!","Stranger"));

setUpAuthenticator();
}

@Test
public void simpleResourceTest() throws Exception {

Saying expectedSaying = new Saying(1,"Hello, Stranger!");

Saying actualSaying = client()
.resource("/hello-world")
.get(Saying.class);

assertEquals("GET hello-world returns a default",expectedSaying.getContent(),actualSaying.getContent());

}


@Test
public void hmacResourceTest() throws Exception {

String authorization = buildHmacAuthorization("/secret", "abc123", "def456");

Saying actual = client()
.resource("/secret")
.header(HttpHeaders.AUTHORIZATION, authorization)
.get(Saying.class);

assertEquals("GET secret returns unauthorized","You cracked the code!", actual.getContent());

}


}

希望这可以帮助您入门。

关于dropwizard - 如何使用 Dropwizard 测试 HMAC 身份验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10783060/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com