Giter VIP home page Giter VIP logo

jersey-hk2's Introduction

Jersey Injection Dependency example with HK2

Example of a Jersey project using the dependency injection framework HK2 to inject logged user into the application via a custum annotation

  • Jersey is a Java Framework that is commonly used to help generate REST Api.
  • HK2 is a lightweight framework which allow Inversion of Control (IoC) and dependency injection (DI)

This project was created to write this article on Medium

TLDR

To start the project, run the server in the Main.java class and hit it on

curl -X GET \
  http://localhost:8080/myapp/myresource \
  -H 'Accept: */*' \
  -H 'Accept-Encoding: gzip, deflate' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDc3MTU4NDQsImV4cCI6MTAwMDAwMTYwNzcxNTg0NCwiaXNzIjoiSVNTVUVSIiwiYXVkIjoiQVVESUVOQ0UiLCJsb2dpbiI6Im00bnU1NiIsImNvbXB0ZSI6ImRldjEwIn0.0Nov_XwHIMOROkiu2cqWxtSd-r_HIQRAXLrAu5pTEv8,Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDc3MTU4NDQsImV4cCI6MTAwMDAwMTYwNzcxNTg0NCwiaXNzIjoiSVNTVUVSIiwiYXVkIjoiQVVESUVOQ0UiLCJsb2dpbiI6Im00bnU1NiIsImNvbXB0ZSI6ImRldjEwIn0.0Nov_XwHIMOROkiu2cqWxtSd-r_HIQRAXLrAu5pTEv8' \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: keep-alive' \
  -H 'Host: localhost:8080' \
  -H 'cache-control: no-cache'

Return the user found in the JWT Token payload

curl -X GET \
  http://localhost:8080/myapp/myresource/users \
  -H 'Accept: */*' \
  -H 'Accept-Encoding: gzip, deflate' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDc3MTU4NDQsImV4cCI6MTAwMDAwMTYwNzcxNTg0NCwiaXNzIjoiSVNTVUVSIiwiYXVkIjoiQVVESUVOQ0UiLCJsb2dpbiI6Im00bnU1NiIsImNvbXB0ZSI6ImRldjEwIn0.0Nov_XwHIMOROkiu2cqWxtSd-r_HIQRAXLrAu5pTEv8,Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDc3MTU4NDQsImV4cCI6MTAwMDAwMTYwNzcxNTg0NCwiaXNzIjoiSVNTVUVSIiwiYXVkIjoiQVVESUVOQ0UiLCJsb2dpbiI6Im00bnU1NiIsImNvbXB0ZSI6ImRldjEwIn0.0Nov_XwHIMOROkiu2cqWxtSd-r_HIQRAXLrAu5pTEv8' \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: keep-alive' \
  -H 'Host: localhost:8080' \
  -H 'cache-control: no-cache'

Returns the list of users

Or use the Postman collection in jersey-hk2.postman_collection.json

Step 1

Generate a project from Maven Archetype:

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example \
-DarchetypeVersion=2.29.1

Step 2

Add some business logic to make this test more interesting in com.example.business:

  • User
  • UserSvc
  • UserDao

Step 3

We want to be able to do Dependency Injection of our services. We need to register a AbstractBinder to our jersey app that will automatically match the given injected class with the implementation.

Create a class ApplicationBinder:

@Provider
public class ApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(JustInTimeServiceResolver.class).to(JustInTimeInjectionResolver.class);
    }
}

And register it with our application: resourceConfig.register(new ApplicationBinder());

And an another class JustInTimeServiceResolver that will handle the automatic binding of services like Google Guice would do.

@Service
public class JustInTimeServiceResolver implements JustInTimeInjectionResolver {

    @Inject
    private ServiceLocator serviceLocator;

    @Override
    public boolean justInTimeResolution(Injectee injectee) {
        final Type requiredType = injectee.getRequiredType();

        if (injectee.getRequiredQualifiers().isEmpty() && requiredType instanceof Class) {
            final Class<?> requiredClass = (Class<?>) requiredType;

            // IMPORTANT: check the package name, so we don't accidentally preempt other framework JIT resolvers
            if (requiredClass.getName().startsWith("com.example")) {
                final List<ActiveDescriptor<?>> descriptors = ServiceLocatorUtilities.addClasses(serviceLocator, requiredClass);

                return !descriptors.isEmpty();
            }
        }
        return false;
    }
}

From there we can already make use of the dependency injection framework. Create a new endpoint to get the list of users:

@GET
@Path("users")
@Produces(MediaType.APPLICATION_JSON)
public List<User> getUsers() {
    return userSvc.getList();
}

And in MyResource inject the UserSvc service this way:

@Inject
private UserSvc userSvc;

We can do the same in the UserSvc class with the field UserDao:

@Inject
private UserDao userDao;

Note that Injected resources need to have a no args constructor.

Wen can test ou API is responding well with the list of our users this way:

@Test
public void testGetUsers() {
    List<User> users = target.path("myresource/users").request().get(new GenericType<List<User>>() {});
    assertEquals(2, users.size());
}

Step 4

Now we would like to be able to get information about the user that send requests to our API and use it where needed in our application. Our API will have secured access managed by a JWT Token.

We will create a @PreMatching Jersey filter that will be run before any request and that:

  • will validate the token that should be in Authorization header
  • will create a user from the claims in the extracted token
  • will put the user in the SecurityContext so that it can be accessed by the HK2 framework
@PreMatching
@Priority(Priorities.AUTHENTICATION)
public class PreMatchingCurrentUserFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) {
        try {
            Jws<Claims> jws = new AuthorizationValidator(false).validate(requestContext);
            AppSecurityContext appSecurityContext = new AppSecurityContext(
                    new HashSet<String>(),
                    new User(
                            (String) jws.getBody().get("login"),
                            (String) jws.getBody().get("compte")
                    ),
                    true
            );
            requestContext.setSecurityContext(appSecurityContext);
        }
        catch (Exception ignored) {
        }
    }
}

And register the filter with our application: resourceConfig.register(new PreMatchingCurrentUserFilter());

Next we will create an new custom annotation that will be used to inject the user anywhere we want.

First create the new annotation, available in Field and in Constructor:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.CONSTRUCTOR})
public @interface CurrentUser {
}

then a new InjectionResolver, where we will bind the User class with the new annotation. We can access the SecurityContext using dependency injection and find the User we injected in the @PreMatching earlier.

public class CurrentUserInjectionResolver implements InjectionResolver<CurrentUser> {

	private javax.inject.Provider<SecurityContext> securityContextProvider;

	@Inject
	public CurrentUserInjectionResolver(
		javax.inject.Provider<SecurityContext> securityContextProvider) {
		this.securityContextProvider = securityContextProvider;
	}

	@Override
	public Object resolve(Injectee injectee, ServiceHandle<?> sh) {
		if (User.class == injectee.getRequiredType()) {
			return securityContextProvider.get().getUserPrincipal();
		}
		return null;
	}

    ...
}

And thats'it, wen can access the user using an injection like that:

@CurrentUser
private User user;

or in a constructor:

@CurrentUser
public UserSvc(User user){
    this.user = user;
}

If user was found in the JWT Token if will be accessible here.

jersey-hk2's People

Contributors

dependabot[bot] avatar ebalpe avatar m4nu56 avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.