RESTEasy Advanced Guide - Filters and Interceptors

  •        0
  

We aggregate and tag open source projects. We have collections of more than one million projects. Check out the projects section.



RESTEasy is JAX-RS 2.1 compliant framework for developing rest applications. It is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It is a fully certified and portable implementation of the JAX-RS 2.1 specification, a JCP specification that provides a Java API for RESTful Web Services over the HTTP protocol. To know how to build basic REST API for CRUD service, you can refer to our previous article.

In this article, we will walk through the Authorization filter, Per resource method filter, GZIP encoding interceptor. All the below examples are developed with RESTEasy 3.7 and can be deployed in Tomcat 8 and above.

Filters and Interceptors

In RESTEasy, filters are used processing for request and response header, whereas interceptors are used for processing request and response body. Server side filters have ContainerRequestFilter which pre-process before JAX-RS resource method and ContainerResponseFilters which post-process after JAX-RS resource method. ReaderInterceptors wrap around the execution of MessageBodyReaders and WriterInterceptors wrap around the execution of MessageBodyWriters.

Authentication and Authorization using Filters

API authentication can be done by implementing ContainerRequestFilter that can cater for @permitall or authenticated resources. It does authorization based on roles also.

If the resource method not annotated with PermitAll, the credentials are verified. Below security filter gets the authorization property from the header and decodes the credentials that is present after BASIC string literal. If the resource method has roleset, authorization check need to be performed with logged in user role. If it fails during the check, request will be aborted with failed response.

@Priority(2000)
public class SecurityFilter implements ContainerRequestFilter {
private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse("User not authorized to access the resource", 403, new Headers<>());
private static final ServerResponse ACCESS_DENIED       = new ServerResponse("Authorization header missing or invalid", 401, new Headers<>());
private static final ServerResponse INTERNAL_SERVER_ERROR = new ServerResponse("Error in decoding credentials", 500, new Headers<>());
private static final String AUTHORIZATION_PROPERTY = "Authorization";

 @Override
 public void filter(ContainerRequestContext containerRequestContext) throws IOException {
     ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker)
     containerRequestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
     Method method = methodInvoker.getMethod();
     if (!method.isAnnotationPresent(PermitAll.class)) {
        if (method.isAnnotationPresent(DenyAll.class)) {
              containerRequestContext.abortWith(ACCESS_FORBIDDEN);
              return;
        }

        final MultivaluedMap<String, String> headers = containerRequestContext.getHeaders();
        final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY);

        if (authorization ==null || authorization.isEmpty()) {
             containerRequestContext.abortWith(ACCESS_DENIED);
             return;
        }

        String credentials = null;
        try {
             credentials = new String(Base64.decode(authorization.get(0).replaceFirst("Basic ", "")));
        }
        catch(Exception exception) {
            containerRequestContext.abortWith(INTERNAL_SERVER_ERROR);
            return;
       }

        final StringTokenizer credentialsParser = new StringTokenizer(credentials, ":");
        String username = credentialsParser.nextToken();
        String password = credentialsParser.nextToken();
        if (username.equals("admin") && password.equals("admin")) {
                System.out.println("!!!Authenticated Successfully!!!");
                String[] rolesAllowed = method.getAnnotation(RolesAllowed.class).value();
                boolean isAllowed = false;
                for (String role: rolesAllowed) {
                    if (role.equals("admin")) {
                         System.out.println("!!Authorized successfully!!");
                         isAllowed = true;
                         break;
                   }
              }
              if (!isAllowed) {
                  containerRequestContext.abortWith(ACCESS_FORBIDDEN);
                  return;
             }
        } else {
           containerRequestContext.abortWith(ACCESS_FORBIDDEN);
           return;
       }
     }
   }
 }

If we define application main as empty singletons, it automatically scans the filter before resource method invocation.

 @ApplicationPath("/rest")
 public class ApplicationMain extends Application {

     @Override
     public Set<Object> getSingletons() {
        return null;
     }
 }

If resource method has @PermitAll annotation then the resource does not require authentication.

  @Path("/message")
  public class MessageRestService {
  @GET
  @Path("/{name}")
  @PermitAll
  public Response printMessage(@PathParam("name") String name) {
       System.out.println("It is invoking rest api service");
       String result = String.format("Hello %s! It is RESTEasy API demo services", name);
       return Response.status(200).entity(result).build();
   }
 }

For all other resource methods which does not have @PermitAll annotation then authentication is required. In this example, we encode the username and password (admin:admin) with base64 and validate the same in the server. In actual production environment, the credentials will be validated against DB in a separate API (Authentication API) which will return JWT token as response and Authorization Filter has to validate the JWT token.

 @PATCH
 @Path("/{id}/mobilenumber/{mobilenumber}")
 @Produces({MediaType.APPLICATION_JSON})
 @RolesAllowed({"admin"})
 public Response updateUserById(@PathParam("id") int id, @PathParam("mobilenumber") String mobilenumber, @Context Request req) {
     CacheControl cc = new CacheControl();
     cc.setMaxAge(1000);
     cc.setMustRevalidate(true);

     EntityTag etag = new EntityTag(employees.get(id).getLastModifiedDate().hashCode()+"");
     Response.ResponseBuilder resp = req.evaluatePreconditions(etag);
     if (resp != null) {
         return resp.tag(etag).build();
     }

     employees.get(id).setMobileNumber(Long.parseLong(mobilenumber));
     employees.get(id).updateDate();
     return Response.status(200).entity("Mobile number updated successfully").cacheControl(cc).build();
 }

Lets test the filters by invoking the API using CURL.

 curl -X PATCH -H 'Authorization: Basic YWRtaW46YWRtaW4=' -v http://localhost:8080/resteasyexamples/rest/employees/1/mobilenumber/9002221111
 * Trying 127.0.0.1...
 * TCP_NODELAY set
 * Connected to localhost (127.0.0.1) port 8080 (#0)
 > PATCH /resteasyexamples/rest/employees/1/mobilenumber/9002221111 HTTP/1.1
 > Host: localhost:8080
 > User-Agent: curl/7.58.0
 > Accept: */*
 > Authorization: Basic YWRtaW46YWRtaW4=
 >
 < HTTP/1.1 200
 < Cache-Control: must-revalidate, no-transform, max-age=1000
 < Content-Type: application/json
 < Content-Length: 34
 < Date: Sun, 26 May 2019 06:32:17 GMT
 <
 * Connection #0 to host localhost left intact
 Mobile number updated successfully

 

Per-Method Filter

Filter methods can be created specifically per-resource method with name binding (@NameBinding) annotation.

 @NameBinding
 @Retention(RetentionPolicy.RUNTIME)
 public @interface ValidateId {
 }

 This @ValidateId annotation is used in filter implementation method and JAX-RS resource method.

  @Provider
  @ValidateId
  public class ValidateIdFilter implements ContainerRequestFilter {
  @Override
  public void filter(ContainerRequestContext containerRequestContext) throws IOException {
       String requestedEmpId = containerRequestContext.getUriInfo().getPathParameters().get("id").get(0);
       try {
           int empId = Integer.parseInt(requestedEmpId);
           if (empId<0 || empId>100) {
               throw new Exception("Invalid Employee Id");
           }
       } catch(Exception exception) {
            containerRequestContext.abortWith(new ServerResponse(exception.getMessage(),500, new Headers<>()));
       }
          System.out.println("Validation filter invoked");
   }
}

Below resource method, it has been annotated with @ValidateId. So while invoking the resource method, filter method corresponding to @ValidateId will be invoked. It validates the employee id to be in range between 1 to 100, if not it aborts the request with failed response.

  @GET
  @Path("/{id}")
  @GZIP
  @Produces({MediaType.APPLICATION_JSON})
  @ValidateId
  @RolesAllowed({"manager","admin"})
   public Response getEmployeeById(@PathParam("id") int id, @Context Request req) {
         CacheControl cc = new CacheControl();
         cc.setMaxAge(1000);
         logger.info(employees.get(id).getLastModifiedDate().toString());
         EntityTag etag = new EntityTag(employees.get(id).getLastModifiedDate().hashCode()+"");
         Response.ResponseBuilder resp = req.evaluatePreconditions(etag);
         if (resp!=null) {
             //response code 402 for precondition failed..
             return resp.cacheControl(cc).tag(etag).build();
         } else {
           System.out.println("Pre-conditon matched");
        }

        logger.info("generating response");
        return Response.status(200).entity(employees.get(id)).cacheControl(cc).tag(etag).build();
  }

When the curl command executed with invalid employee id, it returns the invalid employee id error message.

  curl -H 'Authorization: Basic YWRtaW46YWRtaW4=' -v http://localhost:8080/resteasyexamples/rest/employees/112
  * Trying 127.0.0.1...
  * TCP_NODELAY set
  * Connected to localhost (127.0.0.1) port 8080 (#0)
  > GET /resteasyexamples/rest/employees/112 HTTP/1.1
  > Host: localhost:8080
  > User-Agent: curl/7.58.0
  > Accept: */*
  >
  < HTTP/1.1 500
  < Content-Type: application/json
  < Content-Length: 20
  < Date: Sun, 26 May 2019 06:59:42 GMT
  < Connection: close
  <
  * Closing connection 0
  Invalid Employeed Id

 

GZIP Interceptor

GZIP Interceptor works around message reader and message writer automatically. We need to just annotate the JAX-RS method with @GZIP so response will be compressed and send back to client with lesser content length.

When the getEmployeeById method is invoked after gzip annotated, the response will be compressed and its content length will be reduced.

 curl -X GET -H 'Authorization: Basic YWRtaW46YWRtaW4=' -H 'Accept-Encoding: gzip' -v http://localhost:8080/resteasyexamples/rest/employees --output test.out
 Note: Unnecessary use of  -X or --request, GET is already inferred.
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 127.0.0.1...
 * TCP_NODELAY set
 * Connected to localhost (127.0.0.1) port 8080 (#0)
 > GET /resteasyexamples/rest/employees HTTP/1.1
 > Host: localhost:8080
 > User-Agent: curl/7.58.0
 > Accept: */*
 > Authorization: Basic YWRtaW46YWRtaW4=
 > Accept-Encoding: gzip
 >
 < HTTP/1.1 200
 < Content-Encoding: gzip
 < Content-Type: application/xml;charset=UTF-8
 < Content-Length: 150
 < Date: Sun, 26 May 2019 07:15:24 GMT
 <
 { [150 bytes data]
 100 150 100 150 0 0 4166 0 --:--:-- --:--:-- --:--:-- 4166
 * Connection #0 to host localhost left intact

Reference:

RESTEasy Framework - https://resteasy.github.io/index.html

Code samples - https://github.com/nagappan080810/resteasy_workbook

 


Sponsored:
To find embedded technology information about MCU, IoT, AI etc Check out embedkari.com.


   

Nagappan is a techie-geek and a full-stack senior developer having 10+ years of experience in both front-end and back-end. He has experience on front-end web technologies like HTML, CSS, JAVASCRIPT, Angular and expert in Java and related frameworks like Spring, Struts, EJB and RESTEasy framework. He hold bachelors degree in computer science and he is very passionate in learning new technologies.

Subscribe to our newsletter.

We will send mail once in a week about latest updates on open source tools and technologies. subscribe our newsletter



Related Articles

ETag Easy With RESTEasy

  • resteasy etag http-header rest-api

RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It comprises of frameworks for mock, embeddable server, rest client, proxy servers, logging and so on.In this article, we will walk-through ETag implementation and show the behaviour related to ETag done by rest easy framework. Example is developed using RESTEasy 3.7 and deployed in tomcat as RESTEasy framework is portable.

Read More


RESTEasy Advanced guide - File Upload

  • resteasy rest-api file-upload java

RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It is a fully certified and portable implementation of the JAX-RS 2.1 specification, a JCP specification that provides a Java API for RESTful Web Services over the HTTP protocol. It is licensed under the ASL 2.0.

Read More


RESTEasy - A guide to implement CRUD Rest API

  • resteasy rest-api java framework

RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It is a fully certified and portable implementation of the JAX-RS 2.1 specification, a JCP specification that provides a Java API for RESTful Web Services over the HTTP protocol. It is licensed under the Apache 2.0 license.

Read More


Light4j Cookbook - Rest API, CORS and RDBMS

  • light4j sql cors rest-api

Light 4j is a fast, lightweight and cloud-native microservices framework. In this article, we will see what and how hybrid framework works and integrate with RDMS databases like MySQL, also built in option of CORS handler for in-flight request.

Read More


Introduction to Light 4J Microservices Framework

  • light4j microservice java programming framework

Light 4j is fast, lightweight, secure and cloud native microservices platform written in Java 8. It is based on pure HTTP server without Java EE platform. It is hosted by server UnderTow. Light-4j and related frameworks are released under the Apache 2.0 license.

Read More



Europe Copyright reform may affect Open Source Ecosystem

  • copyright europe save-code-share

Europe is planning to bring in new copyright law, which would require code sharing sites to monitor copyright content to avoid potential copyright infringement. The proposed Article 13 of the EU Copyright Directive targets every online service that allows its users to upload and share content with each other, including code hosting platforms.

Read More


Getting Started on Undertow Server

  • java web-server undertow rest

Undertow is a high performing web server which can be used for both blocking and non-blocking tasks. It is extermely flexible as application can assemble the parts in whatever way it would make sense. It also supports Servlet 4.0, JSR-356 compliant web socket implementation. Undertow is licensed under Apache License, Version 2.0.

Read More


JWT Authentication using Auth0 Library

  • java jwt authentication security

Json Web Token shortly called as JWT becomes defacto standard for authenticating REST API. In a traditional web application, once the user login credentials are validated, loggedin user object will be stored in session. Till user logs out, session will remain and user can work on the web application without any issues. Rest world is stateless, it is difficult to identify whether the user is already authenticated. One way is to use authenticate every API but that would be too expensive task as the client has to provide credentials in every API. Another approach is to use token.

Read More


Exonum Blockchain Framework by the Bitfury Group

  • blockchain bitcoin hyperledger blockchain-framework

Exonum is an extensible open source blockchain framework for building private blockchains which offers outstanding performance, data security, as well as fault tolerance. The framework does not include any business logic, instead, you can develop and add the services that meet your specific needs. Exonum can be used to build various solutions from a document registry to a DevOps facilitation system.

Read More


LogicalDOC - Open Source DMS

  • dms document-management-system

LogicalDOC is both a document management and a collaboration system. The software is loaded with many functions and allows organizing, indexing, retrieving, controlling and distributing important business documents securely and safely for any organization and individual.

Read More


Free Open Source Code Search Engines

  • code-search code-search-engine search-engine

There are couple of sites which indexes the open source code and provides support to search code. Recently Google announced that they removed code search support from Google code. This article provides pointer for code search engine sites.

Read More


Solr vs Elastic Search

  • full-text-search search-engine lucene solr elastic-search

Solr and Elastic Search are built on top of Lucene. Both are open source and both have extra features which makes programmer life easy. This article explains the difference and the best situation to choose between them.

Read More


Advanced Programming Guide in Redis using Jedis

  • redis jedis advanced-guide cluster pipline publish-subscribe

Redis is an in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams. This blog covers the advanced concepts like cluster, publish and subscribe, pipeling concepts of Redis using Jedis Java library.

Read More


AbanteCart - Easy to use open source e-commerce platform, helps selling online

  • e-commerce ecommerce cart

AbanteCart is a free, open source shopping cart that was built by developers with a passion for free and accessible software. Founded in 2010 (launched in 2011), the platform is coded in PHP and supports MySQL. AbanteCart’s easy to use admin and basic layout management tool make this open source solution both easy to use and customizable, depending on the skills of the user. AbanteCart is very user-friendly, it is entirely possible for a user with little to no coding experience to set up and use this cart. If the user would be limited to the themes and features available in base AbanteCart, there is a marketplace where third-party extensions or plugins come to the rescue.

Read More


Caching using Ehcache Java Library

  • ehcache cache java map key-value

Ehcache from Terracotta is one of Java's most widely used Cache. It is concurrent and highly scalable. It has small footprint with SL4J as the only dependencies. It supports multiple strategies like Expiration policies, Eviction policies. It supports three storage tiers, heap, off-heap, disk storage. There are very few caching products supports multiple tier storage. If you want to scale, you cannot store all items in heap there should be support for off-heap and disk storage. Ehcache is licensed under Apache 2.0. In this article, we can see about basic usage of Ehcache.

Read More


Lucene Vs Solr

  • searchengine lucene solr

Lucene is a search library built in Java. Solr is a web application built on top of Lucene. Certainly Solr = Lucene + Added features. Often there would a question, when to choose Solr and when to choose Lucene.

Read More


Best open source Text Editors

  • text-editor editor tools dev-tools

Text editors are mainly used by programmers and developers for manipulating plain text source code, editing configuration files or preparing documentation and even viewing error logs. Text editors is a piece of software which enables to create, modify and delete files that a programmer is using while creating website or mobile app.In this article, we will discuss about top 7 all-round performing text editors which is highly supportive for programmers.

Read More


An introduction to LucidWorks Enterprise Search

  • lucene solr search engine enterprise

Lucidworks Enterprise search solution is built on top of Apache Solr. It scales seamlessly w/sub-second response times under extreme query loads for multi-billion document collections. It has user friendly UI, which does all the job of configuration and search.

Read More


MailHog - Web and API based SMTP testing

  • smtp-testing testing-tool smtp test automation email-server email

Most of the projects will have a requirement of sending and receiving mails. We have mentioned about GreenMail - Email Test Framework, in our previous article about API based SMTP testing. In this article, we discuss about MailHog - Web and API based SMTP testing. You send out a mail from your code and you can check it via web visually and also via API. Those who do API testing can check via API. Developers may want to visually verify the format of the mail. MailHog is a best bet for SMTP testing.

Read More


Cache using Hazelcast InMemory Data Grid

  • hazelcast cache key-value

Hazelcast is an open source In-Memory Data Grid (IMDG). It provides elastically scalable distributed In-Memory computing, widely recognized as the fastest and most scalable approach to application performance.&nbsp;Hazelcast makes distributed computing simple by offering distributed implementations of many developer-friendly interfaces from Java such as Map, Queue, ExecutorService, Lock and JCache.

Read More