Java framework for developing ops-friendly, high-performance, RESTful web applications
—
Pluggable authentication system supporting basic auth, OAuth, and custom authentication schemes with fine-grained authorization control.
Core interface for converting credentials into authenticated principals.
package io.dropwizard.auth;
public interface Authenticator<C, P> {
/**
* Authenticates the given credentials and returns an optional principal.
* @param credentials the credentials to authenticate
* @return the authenticated principal if successful, empty otherwise
* @throws AuthenticationException if authentication fails
*/
Optional<P> authenticate(C credentials) throws AuthenticationException;
}Usage Example:
public class SimpleAuthenticator implements Authenticator<BasicCredentials, User> {
private final UserService userService;
public SimpleAuthenticator(UserService userService) {
this.userService = userService;
}
@Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
if ("secret".equals(credentials.getPassword())) {
return userService.findByUsername(credentials.getUsername());
}
return Optional.empty();
}
}Interface for implementing authorization logic to control access to resources based on roles or permissions.
package io.dropwizard.auth;
public interface Authorizer<P> {
/**
* Determines if the principal is authorized for the given role.
* @param principal the authenticated principal
* @param role the role to check authorization for
* @return true if authorized, false otherwise
*/
boolean authorize(P principal, String role);
}Usage Example:
public class SimpleAuthorizer implements Authorizer<User> {
@Override
public boolean authorize(User user, String role) {
return user.getRoles().contains(role);
}
}Pre-built authentication filters for common authentication schemes.
package io.dropwizard.auth;
public class BasicCredentialAuthFilter<P> extends AuthFilter<BasicCredentials, P> {
public static class Builder<P> extends AuthFilterBuilder<BasicCredentials, P, BasicCredentialAuthFilter<P>> {
public Builder<P> setAuthenticator(Authenticator<BasicCredentials, P> authenticator);
public Builder<P> setAuthorizer(Authorizer<P> authorizer);
public Builder<P> setRealm(String realm);
public BasicCredentialAuthFilter<P> buildAuthFilter();
}
}
public class OAuthCredentialAuthFilter<P> extends AuthFilter<String, P> {
public static class Builder<P> extends AuthFilterBuilder<String, P, OAuthCredentialAuthFilter<P>> {
public Builder<P> setAuthenticator(Authenticator<String, P> authenticator);
public Builder<P> setAuthorizer(Authorizer<P> authorizer);
public Builder<P> setPrefix(String prefix);
public OAuthCredentialAuthFilter<P> buildAuthFilter();
}
}Usage Example:
@Override
public void run(MyConfiguration configuration, Environment environment) {
// Basic Authentication
environment.jersey().register(new AuthDynamicFeature(
new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new SimpleAuthenticator(userService))
.setAuthorizer(new SimpleAuthorizer())
.setRealm("SUPER SECRET STUFF")
.buildAuthFilter()
));
// OAuth Authentication
environment.jersey().register(new AuthDynamicFeature(
new OAuthCredentialAuthFilter.Builder<User>()
.setAuthenticator(new OAuthAuthenticator(tokenService))
.setAuthorizer(new SimpleAuthorizer())
.setPrefix("Bearer")
.buildAuthFilter()
));
// Enable authorization annotations
environment.jersey().register(RolesAllowedDynamicFeature.class);
}Jersey dynamic feature for registering authentication filters with the application.
package io.dropwizard.auth;
public class AuthDynamicFeature implements DynamicFeature {
/**
* Creates an auth dynamic feature with the given auth filter.
*/
public AuthDynamicFeature(AuthFilter<?, ?> authFilter);
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context);
}
@Provider
public class RolesAllowedDynamicFeature implements DynamicFeature {
/**
* Enables @RolesAllowed, @PermitAll, and @DenyAll annotations.
*/
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context);
}JAX-RS security annotations for protecting resources and methods.
// Security annotations
@RolesAllowed({"ADMIN", "USER"})
@PermitAll
@DenyAll
// Authentication context
@Auth Principal principalUsage Example:
@Path("/admin")
@RolesAllowed("ADMIN")
public class AdminResource {
@GET
@Path("/users")
public List<User> getAllUsers(@Auth User currentUser) {
// Only accessible by users with ADMIN role
return userService.findAll();
}
@POST
@Path("/users")
@RolesAllowed({"ADMIN", "MODERATOR"})
public User createUser(@Auth User currentUser, @Valid User newUser) {
// Accessible by ADMIN or MODERATOR roles
return userService.create(newUser);
}
}
@Path("/public")
@PermitAll
public class PublicResource {
@GET
@Path("/health")
public String health() {
// Publicly accessible
return "OK";
}
}Creating custom authentication filters for specialized authentication requirements.
package io.dropwizard.auth;
public abstract class AuthFilter<C, P> implements ContainerRequestFilter {
/**
* Extracts credentials from the request.
*/
public abstract C getCredentials(ContainerRequestContext requestContext);
/**
* Called when authentication fails.
*/
public abstract void onAuthenticationFailure(String challenge,
ContainerRequestContext requestContext);
/**
* Called when authorization fails.
*/
public abstract void onAuthorizationFailure(String challenge,
ContainerRequestContext requestContext);
}
public abstract class AuthFilterBuilder<C, P, T extends AuthFilter<C, P>> {
public abstract T buildAuthFilter();
protected AuthFilterBuilder<C, P, T> setAuthenticator(Authenticator<C, P> authenticator);
protected AuthFilterBuilder<C, P, T> setAuthorizer(Authorizer<P> authorizer);
}Usage Example:
public class ApiKeyAuthFilter extends AuthFilter<String, User> {
@Override
public String getCredentials(ContainerRequestContext requestContext) {
return requestContext.getHeaderString("X-API-Key");
}
@Override
public void onAuthenticationFailure(String challenge,
ContainerRequestContext requestContext) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, challenge)
.entity("Invalid API key")
.build()
);
}
@Override
public void onAuthorizationFailure(String challenge,
ContainerRequestContext requestContext) {
requestContext.abortWith(
Response.status(Response.Status.FORBIDDEN)
.entity("Insufficient permissions")
.build()
);
}
public static class Builder extends AuthFilterBuilder<String, User, ApiKeyAuthFilter> {
@Override
protected ApiKeyAuthFilter newInstance() {
return new ApiKeyAuthFilter();
}
}
}Wrapper authenticators that cache authentication results to improve performance.
package io.dropwizard.auth;
public class CachingAuthenticator<C, P> implements Authenticator<C, P> {
/**
* Creates a caching authenticator with the given cache spec.
*/
public static <C, P> CachingAuthenticator<C, P> wrap(
Authenticator<C, P> underlying,
CacheBuilderSpec cacheSpec);
/**
* Creates a caching authenticator with a cache builder.
*/
public static <C, P> CachingAuthenticator<C, P> wrap(
Authenticator<C, P> underlying,
CacheBuilder<Object, Object> cacheBuilder);
}Usage Example:
@Override
public void run(MyConfiguration configuration, Environment environment) {
// Cache authentication results for 10 minutes
Authenticator<BasicCredentials, User> authenticator =
CachingAuthenticator.wrap(
new DatabaseAuthenticator(userService),
CacheBuilderSpec.parse("maximumSize=1000, expireAfterWrite=10m")
);
environment.jersey().register(new AuthDynamicFeature(
new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(authenticator)
.setAuthorizer(new SimpleAuthorizer())
.setRealm("My Service")
.buildAuthFilter()
));
}Example implementation for JSON Web Token authentication.
public class JWTAuthenticator implements Authenticator<String, User> {
private final JWTVerifier verifier;
private final UserService userService;
public JWTAuthenticator(JWTVerifier verifier, UserService userService) {
this.verifier = verifier;
this.userService = userService;
}
@Override
public Optional<User> authenticate(String token) throws AuthenticationException {
try {
DecodedJWT jwt = verifier.verify(token);
String username = jwt.getSubject();
return userService.findByUsername(username);
} catch (JWTVerificationException e) {
return Optional.empty();
}
}
}Combining multiple authentication factors for enhanced security.
public class MFAAuthenticator implements Authenticator<BasicCredentials, User> {
private final Authenticator<BasicCredentials, User> primaryAuth;
private final TOTPService totpService;
@Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
// First factor: username/password
Optional<User> user = primaryAuth.authenticate(credentials);
if (!user.isPresent()) {
return Optional.empty();
}
// Second factor: TOTP code (assumed to be in password field after |)
String[] parts = credentials.getPassword().split("\\|");
if (parts.length != 2) {
return Optional.empty();
}
String totpCode = parts[1];
if (!totpService.verify(user.get().getTotpSecret(), totpCode)) {
return Optional.empty();
}
return user;
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-dropwizard--dropwizard-project