Using dropwizard, we have learned about creating REST APIs, writing client code and adding health-check filters. In this tutorial, we will learn to add username/password based authentication and role based authorization capabilities into REST APIs using basic authentication.
1. Maven
Authentication capabilities are added as a separate module in the dropwizard application.
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-auth</artifactId>
<version>4.0.0</version>
</dependency>
2. Security Principal
In security, the principal object represents the user for which credentials have been supplied with the request. It implements java.security.Principal
interface.
package com.howtodoinjava.app.auth;
import java.security.Principal;
import java.util.Set;
public class User implements Principal {
private final String name;
private final Set<String> roles;
public User(String name) {
this.name = name;
this.roles = null;
}
public User(String name, Set<String> roles) {
this.name = name;
this.roles = roles;
}
public String getName() {
return name;
}
public int getId() {
return (int) (Math.random() * 100);
}
public Set<String> getRoles() {
return roles;
}
}
3. Custom Authenticator
Authenticator
class is responsible for verifying username/password credentials included in Basic Auth header. In enterprise applications, you may fetch the user’s password from the database, and if it matches then you set the user roles into a principal object. In dropwizard, you will need to implement io.dropwizard.auth.Authenticator
interface to put your application logic.
package com.howtodoinjava.app.auth;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
public class AppBasicAuthenticator implements Authenticator<BasicCredentials, User> {
private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
"guest", ImmutableSet.of(),
"user", ImmutableSet.of("USER"),
"admin", ImmutableSet.of("ADMIN", "USER")
);
@Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
if (VALID_USERS.containsKey(credentials.getUsername())
&& "password".equals(credentials.getPassword())) {
return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
}
return Optional.empty();
}
}
4. Custom Authorizer
Authorizer
class is responsible for matching the roles and deciding whether the user is allowed to perform a certain action or not.
package com.howtodoinjava.app.auth;
import io.dropwizard.auth.Authorizer;
import jakarta.ws.rs.container.ContainerRequestContext;
import org.checkerframework.checker.nullness.qual.Nullable;
public class AppAuthorizer implements Authorizer<User> {
@Override
public boolean authorize(User user, String role,
@Nullable ContainerRequestContext containerRequestContext) {
return user.getRoles() != null && user.getRoles().contains(role);
}
}
5. Configure BasicCredentialAuthFilter
Now let’s register our custom classes into the dropwizard security framework.
public class App extends Application<ApplicationConfiguration> {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
@Override
public void initialize(Bootstrap<ApplicationConfiguration> b) {
}
@Override
public void run(ApplicationConfiguration c, Environment e) throws Exception {
//...
//****** Dropwizard security ***********/
e.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new AppBasicAuthenticator())
.setAuthorizer(new AppAuthorizer())
.setRealm("BASIC-AUTH-REALM")
.buildAuthFilter()));
e.jersey().register(RolesAllowedDynamicFeature.class);
e.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
public static void main(String[] args) throws Exception {
new App().run(args);
}
}
6. Securing REST APIs with Annotations
6.1. @Auth to Trigger Authentication
Adding @Auth
annotation will trigger an authentication filter on any API where it is put as a parameter.
@GET
@PermitAll
public Response getEmployees(@Auth User user) {
return Response.ok(repository.getEmployees()).build();
}
6.2. Jakarta Security Annotation for Authorization
The jakarta security annotations can be used to configure what is allowed to a user having a specific role or not.
@GET
@Path("/{id}")
@PermitAll
public Response getEmployeeById(@PathParam("id") Integer id, @Auth User user) {
//...
}
@DELETE
@Path("/{id}")
@RolesAllowed({"ADMIN"})
public Response removeEmployeeById(@PathParam("id") Integer id, @Auth User user) {
//...
}
In this way, you can add various authentication schemes in all your APIs as needed.
7. Demo
Let’s test out our secured APIs.
Call Any secured API

http://localhost:8080/employees

http://localhost:8080/employees/1

Drop me your questions in the comments section.
Happy Learning !!
Comments