JAX-RS web services work in Liferay modules the same way they work outside of Liferay. The only difference is that you must register the class in the OSGi framework. Liferay makes this easy by providing a template.
Create a project and choose the rest template.
The class that’s generated contains a working JAX-RS web service. You can deploy it and use it immediately.
While it’s beyond the scope of this article to cover
JAX-RS Whiteboard
in its entirety, essentially it’s JAX-RS unchanged except for configuration
properties in the @Component
annotation. These properties declare three things:
-
The endpoint for the service
-
The service name as it appears in the OAuth 2.0 configuration
-
(Optional) Properties you may want to set for further configuration.
The generated class contains this configuration:
@Component(
property = {
JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greetings",
JaxrsWhiteboardConstants.JAX_RS_NAME + "=Greetings.Rest"
},
service = Application.class)
This configuration registers the service at this endpoint:
https://[server-name]:[port]/o/greetings
If you’re testing this locally on Tomcat, the URL is
https://localhost:8080/o/greetings
As you might guess, you don’t have access to the service by just calling the URL above. You must authenticate first, which you’ll learn how to do next.
Authenticating to JAX-RS Web Services
Authentication during development can be done through Basic Authentication or portal sessions, but you don’t want to leave that enabled for production. For production, you want OAuth 2.0. Here’s how to configure JAX-RS authentication.
During Development: Basic Auth
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, to
disable guest access to the service, configure it like this:
@Component(
property = {
JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greetings",
JaxrsWhiteboardConstants.JAX_RS_NAME + "=Greetings.Rest",
"auth.verifier.guest.allowed=false"
},
service = Application.class)
Basic Auth is great during development, but credentials passed on the URL appear
in server logs, so when you’re done developing, you should disable Basic Auth
and use OAuth2 instead. To disable Basic Auth, create and deploy a configuration
file called
com.liferay.portal.security.auth.verifier.internal.tracker.AuthVerifierFilterTracker.config
that contains this property:
default.registration.property=["filter.init.auth.verifier.OAuth2RESTAuthVerifier.urls.includes=*","filter.init.auth.verifier.PortalSessionAuthVerifier.urls.includes=*"]
This disables Basic Auth for all JAX-RS applications, but keeps Portal Session and OAuth2 enabled.
Using OAuth 2.0 to Invoke a JAX-RS Web Service
Your JAX-RS web service requires authorization by default. To enable this, you must create an OAuth 2.0 application to provide a way to grant access to your service:
-
Go to the Control Panel → Configuration → OAuth2 Administration and click the button to add an application.
-
Give your application a descriptive name.
-
Choose the Client Profile appropriate for this service. These are templates that auto-select the appropriate authorization types or “flows” from the OAuth 2 standard. For this example choose the Headless Server profile, which auto-selects the Client Credentials authorization type.
-
Click Save.
The form now reappears with two additional generated fields: Client ID and Client Secret. You’ll use these to authenticate to your web service.
To make your service accessible,
-
Click the Scopes tab.
-
You’ll see an entry for your deployed
Greetings.Rest
service. Expand it by clicking the arrow. -
Check the box labeled read data on your behalf.
-
Click Save.
For simplicity, the examples below use Curl to authenticate. You need the two pieces of information generated for your application: the Client ID and the Client Secret. For example, say those fields contain these values:
Client ID: id-12e14a84-e558-35a7-cf9a-c64aafc7f
Client Secret: secret-93f14320-dc39-d67f-9dec-97717b814f
First, you must request an OAuth token. If you’re testing locally, you’d make a request like this:
curl http://localhost:8080/o/oauth2/token -d 'grant_type=client_credentials&client_id=id-12e14a84-e558-35a7-cf9a-c64aafc7f&client_secret=secret-93f14320-dc39-d67f-9dec-97717b814f'
The response is JSON:
{"access_token":"a7f12bef7f2e578cf64bce4085db8f17b6a3c2963f865a65b374e89784bbca5","token_type":"Bearer","expires_in":600,"scope":"GET POST PUT"}
It contains a token, generated for this client. 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 a7f12bef7f2e578cf64bce4085db8f17b6a3c2963f865a65b374e89784bbca5" http://localhost:8080/o/greetings/morning
With authorization, your web service can be called and responds to the request:
Good morning!
Of course, this is only one of the authorization flows for OAuth 2.0. If you’re creating a web-based client whose back-end is a JAX-RS web service hosted on Liferay DXP, you’d want one of the other flows. See the OAuth 2.0 documentation for further information. Additionally, OAuth 2.0 assumes the use of HTTPS for its security: the above URLs are only for local testing purposes. You certainly would not want to pass OAuth tokens between clients and servers in the clear. Make sure that in production your server uses HTTPS.
OAuth2 Scopes
Without any special Liferay OAuth2 annotations or properties, a standard OSGi JAX-RS application is inspected by the Liferay OAuth2 runtime, and scopes are derived by default based on the HTTP verbs supported by the application.
When developers want more control, they can use the property
oauth2.scopechecker.type=annotations
and the annotation
com.liferay.oauth2.provider.scope.RequiresScope
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:
oauth2.scope.checker.type=none
Requiring OAuth2
You can specify OAuth2 authorization as required for your JAX-RS application by using this property:
osgi.jaxrs.extension.select=(osgi.jaxrs.name=Liferay.OAuth2)
JAX-RS and Service Access Policies
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, as by default, they allow invoking all remote services. To
disable Service Access Policy enforcement for JAX-RS endpoints (not recommended),
set this property:
liferay.access.control.disable=true
With this configured, guests can call these endpoints without administrators having to define a default Service Access Policy.
Public JAX-RS Services
To create a public endpoint for development purposes, all you must do is set two properties:
@Component(
property={
"auth.verifier.guest.allowed=true",
"liferay.access.control.disable=true"
},
service = Application.class
)
Don’t keep this configuration for production. For public services, it’s best to leave the security in place and whitelist the particular endpoints you’re making public. See Service Access Policies for further information.
Using JAX-RS with CORS
If you foresee that JavaScript in a browser might access your JAX-RS web service
from a different domain, you might want to use the CORS annotation. You can use
the @CORS
annotation to define
CORS policies on your deployed
JAX-RS applications. Note that the annotations
can be overridden by an administrator.
It only takes three steps:
- Add the Portal Remote CORS API dependency to your module:
compileOnly project(":apps:portal-remote:portal-remote-cors-api")
- 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
)
public class MyApplication extends Application {
...
}
- Use the
@CORS
annotation throughout your application globally or by method.
Globally:
@Component(
property = {
"osgi.jaxrs.application.base=/my-application",
"osgi.jaxrs.name=My.Application.Name",
"liferay.cors.annotation=true"
},
service = Application.class
)
@CORS(allowMethods="GET")
public class MyApplication extends Application {
...
}
By method:
@CORS
@GET
@Path("/users")
public List<User> getUserList() throws Exception {
return _users;
}
You can use the annotation to provide a configuration for any of the CORS headers. Here are some examples:
Header | Annotation 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") |
If for some reason you want to disable the @CORS
annotations in your
application, you can do it globally by disabling it in your @Component
annotation:
@Component(
property = {
"osgi.jaxrs.application.base=/no-cors-application",
"osgi.jaxrs.name=NoCors.Application.Name",
"liferay.cors.annotation=false"
},
service = Application.class
)
Great! Now you know how to create, deploy, and invoke JAX-RS web services on Liferay DXP’s platform!