-
-
Notifications
You must be signed in to change notification settings - Fork 49
Authentication integration
DEPRECATED Documentation was moved into new docuemntation site
Page describes how to configure dropwizard authentication with guice support.
@Provider
class OAuthDynamicFeature extends AuthDynamicFeature {
@Inject
OAuthDynamicFeature(OAuthAuthenticator authenticator, OAuthAuthorizer authorizer, Environment environment) {
super(new OAuthCredentialAuthFilter.Builder<User>()
.setAuthenticator(authenticator)
.setAuthorizer(authorizer)
.setPrefix("Bearer")
.buildAuthFilter())
environment.jersey().register(RolesAllowedDynamicFeature.class)
environment.jersey().register(new AuthValueFactoryProvider.Binder(User.class))
}
// may be external class (internal for simplicity)
@Singleton
public static class OAuthAuthenticator implements Authenticator<String, User> {
@Override
Optional<User> authenticate(String credentials) throws AuthenticationException {
return Optional.fromNullable(credentials == "valid" ? new User(name: "valid") : null)
}
}
// may be external class (internal for simplicity)
@Singleton
public static class OAuthAuthorizer implements Authorizer<User> {
@Override
public boolean authorize(User user, String role) {
return user.getName().equals("good-guy") && role.equals("ADMIN");
}
}
}
If auto configuration is enabled, then class will be resolved and installed automatically. For manual mode:
bootstrap.addBundle(GuiceBundle.<TestConfiguration> builder()
.installers(JerseyProviderInstaller.class, ResourceInstaller.class)
.extensions(OAuthDynamicFeature.class, MySecuredResource.class)
.build()
);
The only thing which really need to be guice-aware is your custom authenticator:
@Singleton
public class SimpleAuthenticator implements Authenticator<BasicCredentials, User> {
@Inject
private MyAuthService authService;
@Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
return Optional.fromNullable(authService.findUser(credentials.getUsername(), credentials.getPassword());
}
}
The other part is configured in application run method (as in manual):
@Override
public void run(ExampleConfiguration configuration,
Environment environment) {
environment.jersey().register(AuthFactory.binder(new BasicAuthFactory<String>(
InjectorLookup.getInjector(this).get().getInstance(SimpleAuthenticator.class),
"SUPER SECRET STUFF",
User.class)));
}
Here we simply obtain authenticator instance from guice context.
Note: dropwizard *AuthFactory classes (BasicAuthFactory, OAuthFactory) are final, so can't be extended and adopted for guice injection directly. That's why "standard" configuration is the best way.
When you write completely custom AuthFactory, it could be described as guice beans.
For example, if we need even custom annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.PARAMETER, ElementType.FIELD])
@Documented
public @interface CustomAuth {
// suppose we need custom annotation to support custom annotation field
String value() default "custom"
}
Our custom auth factory:
@Provider
public class AuthFactory implements Factory<User>{
@Inject
private Provider<HttpServletRequest> request;
@Override
User provide() {
// do something with request and provide or not user
return new User();
}
@Override
void dispose(User instance) {
}
}
External authenticator may be used if required (as in dropwizard factories). Depends on your requirements.
Next we need to associate custom auth annotation with auth factory:
@Provider
@LazyBinding // @HK2Managed may be used as alternative
class AuthFactoryProvider extends AbstractValueFactoryProvider {
private Factory<User> authFactory;
@Inject
public AuthFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final Factory<User> factory, // also Provider<AuthFactory> could be used
final ServiceLocator injector) {
super(extractorProvider, injector, Parameter.Source.UNKNOWN);
this.authFactory = factory;
}
@Override
protected Factory<?> createValueFactory(Parameter parameter) {
final CustomAuth auth = parameter.getAnnotation(CustomAuth.class);
return auth != null ? authFactory : null
}
}
Note @LazyBinding
annotation here. It is required because class depends on hk beans, which are not available in time of guice injector creation. Annotated beans are not created in time of context creation and will be created only when HK start to init auth services.
As an alternative, @HK2Managed
could be used, which will delegate bean creation into HK2. HK started after guice and could inject guice dependencies.
And the last, we need to define custom injection resolver so jersey could associate annotated parameter with our auth services:
@Provider
@LazyBinding
public class AuthInjectionResolver extends ParamInjectionResolver<Auth> {
public AuthInjectionResolver() {
super(AuthFactoryProvider.class)
}
}
All classes are annotated with jersey @Provider
annotation and will be installed by JerseyProviderInstaller
.
If you use classpath scan, then these classes must lie in the scanned packages.
In case of manual configuration, JerseyProviderInstaller installer must be installed and classes registered.
After all, custom annotation may be used in resources:
@GET
@Path("/")
public Response authDemo(@CustomAuth("customKey") User user) {
return Response.ok().build();
}