I\'m trying to understand how authentication and authorization work in DropWizard. I\'ve read their auth guide as well as the dropwizard-security project on GitHub, but feel
Basic Authentication protocol states the client request should have a header in the form of
Authorization: Basic Base64Encoded(username:password)
where Base64Encoded(username:password)
is an actual Base64 encoded string of the username:password
. For example, if my username and password are peeskillet:pass
, the header should be sent out as
Authorization: Basic cGVlc2tpbGxldDpwYXNz
That being said, the Jersey Client (assuming 1.x) has an HTTPBasicAuthFilter
, which is a client side filter, that will handle the encoding part for us. So the client side request might look something like
Client client = Client.create();
WebResource resource = client.resource(BASE_URI);
client.addFilter(new HTTPBasicAuthFilter("peeskillet", "pass"));
String response = resource.get(String.class);
That's all we would need to make a simple GET request with the authorization header.
SimpleCredential: For Basic auth, we would actually be required to use BasicCredentials
, instead of our own credentials. Basically, the request will go through the BasicAuthProvider
. The provider will parse the Authorization header and create a BasicCredentials
object from the parsed username and password. Once that processing has finished, the BasicCredentials
will get passed to our SimpleAuthenticator
's. We use those credentials to authenticate the user.
SimplePrincipal: is basically what we will use to authorize the client. From the authentication process, we can build a principal, that will be used to authorize later (see Question 3). So an example might look something like
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);
}
}
I altered the SimplePrincipal
class a bit, and created a simple Roles
class.
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";
}
Some might prefer to have an extra filter layer for authorization, but Dropwizard appears to have the opinionated view that the authorization should occur in the resource class (I forgot exactly where I read it, but I believe their argument is testability). What happens with the SimplePrincial
that we created in the SimpleAuthenticator
is that it can be injected into our resource method, with the use of the @Auth
annotations. We can use the SimplePrincipal
to authorize. Something like
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();
}
}
So putting it all together, with this configuration
environment.jersey().register(new BasicAuthProvider<SimplePrincipal>(
new SimpleAuthenticator(),
"Basic Example Realm")
);
and the client credentials I posted previously, when we make the request, we should get a returned
{"Hello": "peeskillet"}
Also it should be mentioned that Basic auth alone is not secure, and it is recommended to be done over SSL
See Related:
A couple things:
For Dropwizard 0.8.x, the configuration of Basic Auth has changed a bit. You can see more here. A simple example would be
SimpleAuthenticator auth = new SimpleAuthenticator();
env.jersey().register(AuthFactory.binder(
new BasicAuthFactory<>(auth,"Example Realm",SimplePrincipal.class)));
See above link for recommended usage of AuthenticationException