JAX-RS

JAX-RS web services work in Liferay modules the same way they work outside of Liferay, though you must register the class in the OSGi framework. To learn how to create these applications, see Jakarta’s documentation. Here is shown how to integrate and authenticate JAX-RS applications in Liferay.

Deploy an Example REST API

This example deploys an example API that returns a test product by its ID in a catalog. Once you understand how this example works, you can create APIs for your own applications.

Start a new Liferay instance by running

docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.132-ga132

Sign in to Liferay at http://localhost:8080. Use the email address test@liferay.com and the password test. When prompted, change the password to learn.

Then, follow these steps:

  1. Download and unzip the liferay-u6b1.zip example project:

    curl https://resources.learn.liferay.com/examples/liferay-u6b1.zip -O
    
    unzip liferay-u6b1.zip
    
  2. Build and deploy the example:

    ./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    
    Note

    This command is the same as copying the deployed jars to /opt/liferay/osgi/modules on the Docker container.

  3. Confirm the deployment in the Docker container console:

    STARTED com.liferay.headless.admin.site.internal.jaxrs.application_1.0.0
    

Authenticating JAX-RS Web Services

There are two ways to authenticate: Basic Authentication and OAuth 2.0. Basic Authentication should never be used in production; it’s there for a developer’s convenience during development, because it’s easier to use. Because credentials passed on the URL appear in server logs, credentials could be leaked to those with access to the logs.

When authenticating via Basic Auth, the Service Access Policy SYSTEM_USER_PASSWORD is enforced. When authenticating via OAuth 2.0, the AUTHORIZED_OAUTH2_SAP policy is enforced. Configure them appropriately for your environment since, by default, they allow invoking all remote services. To disable Service Access Policy enforcement for JAX-RS endpoints so guests can call these endpoints without a default Service Access Policy, set the liferay.access.control.disable property to true:

@Component(
    property = {
        JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greeting",
        JaxrsWhiteboardConstants.JAX_RS_NAME + "=Greeting.Rest",
        "liferay.access.control.disable=true"
    },
    service = Application.class)
Important

Disabling Service Access Policy enforcement is not recommended. You must remember to re-enable it before putting your code in production.

You can disable Basic Authentication for all JAX-RS applications, while keeping OAuth 2.0 and Portal Session Authentication enabled by setting the following property in the META-INF/services/com.liferay.portal.security.auth.verifier.internal.tracker.AuthVerifierFilterTracker.config file:

default.registration.property=["filter.init.auth.verifier.OAuth2RESTAuthVerifier.urls.includes=*","filter.init.auth.verifier.PortalSessionAuthVerifier.urls.includes=*"]

During Development: Using Basic Authentication

When you deploy a JAX-RS application, an Auth Verifier filter is registered for it. You can set its properties in your @Component annotation by prefixing the properties with auth.verifier. For example, use this configuration to disable guest access to the service:

@Component(
    property = {
        JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greeting",
        JaxrsWhiteboardConstants.JAX_RS_NAME + "=Greeting.Rest",
        "auth.verifier.guest.allowed=false"
    },
    service = Application.class)
Tip

If you disable guest access like this and disable Service Access Policy enforcement, your endpoints are made completely public. This is not recommended for production. Instead, it is recommended to whitelist the specific endpoints you’re making pubic.

Using OAuth 2.0

Your JAX-RS web service requires authorization by default. To enable this, you must first create an OAuth 2.0 application to provide a way to grant access to your service. Choose the Headless Server profile, which uses the Client Credentials authorization type so your service can be called non-interactively. Then, to make your service accessible,

  1. Open your new OAuth 2.0 application.

  2. Click the Scopes tab.

  3. Expand the Greeting.Rest service by clicking the arrow.

  4. Check the box labeled read data on your behalf.

  5. Click Save.

Next, you must request an OAuth token using the Client ID and the Client Secret generated for your new OAuth 2.0 application. For simplicity, the examples below use Curl to authenticate. If you’re testing locally, make a request like this:

curl http://localhost:8080/o/oauth2/token -d 'grant_type=client_credentials&client_id=[Your Client ID here]&client_secret=[Your Client Secret here]'

The response in JSON contains a token generated for this client. For example,

{"access_token":"a7f12bef7f2e578cf64bce4085db8f17b6a3c2963f865a65b374e89784bbca5","token_type":"Bearer","expires_in":600,"scope":"GET POST PUT"}

It expires in 600 seconds, and it grants GET, POST, and PUT for this web service. When you want to call the service, you must supply the token in the HTTP header, like this:

curl --header "Authorization: Bearer [Your access token here]" http://localhost:8080/o/greeting

With authorization, you can call your web service, and it responds to the request:

Hello, World!

Curl is one of many ways to authenticate with OAuth 2.0 and it’s not recommended in production environments. See Using OAuth 2.0 for more information.

OAuth 2.0 Scopes

In a standard JAX-RS application without OAuth 2.0 annotations or properties, scopes are derived based on the HTTP verbs supported by the application. To specify scopes, use the oauth2.scope.checker.type=annotations property and the com.liferay.oauth2.provider.scope.RequiresScope annotation exported from the Liferay OAuth2 Provider Scope API bundle to annotate endpoint resource methods or whole classes like this:

@RequiresScope("scopeName")

Once deployed, this becomes a scope in the OAuth 2.0 configuration. You can disable scope checking (not recommended) by setting the scope checker to a non-existent type:

@Component(
    property = {
        JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greeting",
        JaxrsWhiteboardConstants.JAX_RS_NAME + "=Greeting.Rest",
        "oauth2.scope.checker.type=none"
    },
    service = Application.class)

You can specify OAuth 2.0 authorization as required for your JAX-RS application by using this property in the @Component annotation:

osgi.jaxrs.extension.select=(osgi.jaxrs.name=Liferay.OAuth2)

Using JAX-RS with CORS

You can use the @CORS annotation to define CORS policies on your deployed JAX-RS applications so they can be accessed from a different domain:

  1. Add the Portal Remote CORS API dependency to your module’s build.gradle file:
compileOnly project(":apps:portal-remote:portal-remote-cors-api")
  1. Activate the CORS annotation feature in your application properties:
@Component(
    property = {
        "osgi.jaxrs.application.base=/my-application",
        "osgi.jaxrs.name=My.Application.Name",
        "liferay.cors.annotation=true"
    },
    service = Application.class
    )
  1. Use the @CORS annotation throughout your application globally or by method.

Globally:

@CORS(allowMethods="GET")
public class HeadlessAdminSiteApplication extends Application {

By method:

@CORS
	@GET
	@Path("/users")
	public List<User> getUserList() throws Exception {
		return _users;
	}
Note

These annotations can be overridden by an administrator.

You can use the annotation to provide a configuration for any of the CORS headers:

HeaderAnnotation Example
Access-Control-Allow-Credentials@CORS(allowCredentials = false)
Access-Control-Allow-Headers@CORS(allowHeaders = "X-PINGOTHER")
Access-Control-Allow-Methods@CORS(allowMethods = "OPTIONS,POST")
Access-Control-Allow-Origin@CORS(allowOrigin = "http://www.liferay.com")

Great! Now you know how to create, deploy, and invoke JAX-RS web services in Liferay!