问题 1:
基本身份验证协议规定客户端请求应具有以下形式的标头
Authorization: Basic Base64Encoded(username:password)
其中 是 的实际 Base64 编码字符串。例如,如果我的用户名和密码是 ,则标头应作为Base64Encoded(username:password)
username:password
peeskillet:pass
Authorization: Basic cGVlc2tpbGxldDpwYXNz
话虽如此,泽西客户端(假设1.x)有一个,这是一个客户端过滤器,它将为我们处理编码部分。因此,客户端请求可能看起来像这样HTTPBasicAuthFilter
Client client = Client.create();
WebResource resource = client.resource(BASE_URI);
client.addFilter(new HTTPBasicAuthFilter("peeskillet", "pass"));
String response = resource.get(String.class);
这就是我们使用授权标头发出简单 GET 请求所需的全部内容。
问题 2:
简单信:对于基本身份验证,我们实际上需要使用 ,而不是我们自己的凭据。基本上,请求将通过 .提供程序将解析授权标头,并根据解析的用户名和密码创建对象。一旦处理完成,将传递给我们的 ' 。我们使用这些凭据对用户进行身份验证。BasicCredentials
BasicAuthProvider
BasicCredentials
BasicCredentials
SimpleAuthenticator
简单原则:基本上是我们将用于授权客户端的内容。从身份验证过程中,我们可以构建一个主体,稍后将用于授权(请参阅问题 3)。因此,示例可能如下所示
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,
SimplePrincipal> {
@Override
public Optional<SimplePrincipal> authenticate(BasicCredentials credentials)
throws AuthenticationException {
// Note: this is horrible authentication. Normally we'd use some
// service to identify the password from the user name.
if (!"pass".equals(credentials.getPassword())) {
throw new AuthenticationException("Boo Hooo!");
}
// from some user service get the roles for this user
// I am explicitly setting it just for simplicity
SimplePrincipal prince = new SimplePrincipal(credentials.getUsername());
prince.getRoles().add(Roles.ADMIN);
return Optional.fromNullable(prince);
}
}
我稍微修改了一下类,并创建了一个简单的类。SimplePrincipal
Roles
public class SimplePrincipal {
private String username;
private List<String> roles = new ArrayList<>();
public SimplePrincipal(String username) {
this.username = username;
}
public List<String> getRoles() {
return roles;
}
public boolean isUserInRole(String roleToCheck) {
return roles.contains(roleToCheck);
}
public String getUsername() {
return username;
}
}
public class Roles {
public static final String USER = "USER";
public static final String ADMIN = "ADMIN";
public static final String EMPLOYEE = "EMPLOYEE";
}
问题 3:
有些人可能更喜欢有一个额外的过滤层用于授权,但Dropwizard似乎有一种固执己见的观点,即授权应该发生在资源类中(我忘记了我在哪里阅读它,但我相信他们的论点是可测试性)。我们在 中创建的 会发生什么情况是,通过使用注释,它可以注入到我们的资源方法中。我们可以使用 授权。类似的东西SimplePrincial
SimpleAuthenticator
@Auth
SimplePrincipal
import dropwizard.sample.helloworld.security.Roles;
import dropwizard.sample.helloworld.security.SimplePrincipal;
import io.dropwizard.auth.Auth;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/simple")
public class SimpleResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getResponse(@Auth SimplePrincipal principal) {
if (!principal.isUserInRole(Roles.ADMIN)) {
throw new WebApplicationException(Response.Status.FORBIDDEN);
}
return Response.ok(
"{\"Hello\": \"" + principal.getUsername() + "\"}").build();
}
}
因此,通过此配置将其全部放在一起
environment.jersey().register(new BasicAuthProvider<SimplePrincipal>(
new SimpleAuthenticator(),
"Basic Example Realm")
);
和我之前发布的客户端凭据,当我们提出请求时,我们应该得到一个返回
{"Hello": "peeskillet"}
另外应该提到的是,仅靠基本身份验证是不安全的,建议通过SSL完成
请参阅相关内容:
更新
几件事:
-
对于 Dropwizard 0.8.x,Basic Auth 的配置发生了一些变化。您可以在此处查看更多内容。一个简单的例子是
SimpleAuthenticator auth = new SimpleAuthenticator();
env.jersey().register(AuthFactory.binder(
new BasicAuthFactory<>(auth,"Example Realm",SimplePrincipal.class)));
请参阅上面的链接,了解建议的用法AuthenticationException