Securing JAX-RS applications within the web container

You can use the security services available from the web container to secure Representational State Transfer (REST) resources. You can configure security mechanisms that define user authentication, transport security, authorization control, and user to role mappings.

Before you begin

To appropriately define security constraints, it is important that you are familiar with your application and the RESTful resources that it exposes. This knowledge helps you to determine appropriate security roles required by your application as well as the individual resources it exposes.

To illustrate how to secure a REST application, this topic uses a sample REST application called AddressBookApp.

You must complete the installation of your application on the application server. For example, after you install the AddressBookApp application, the AddressBookApp deployment descriptor found in the profile_root/config/cells/cellName/applications/applicationName.ear/deployments/applicationName_war/applicationName.war/WEB-INF directory looks like the following example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp_1255468655347">
 <display-name>Sample REST Web Application</display-name>
  <servlet>
    <servlet-name>AddressBookApp</servlet-name>
    <servlet-class>com.ibm.websphere.jaxrs.server.IBMRestServlet</servlet-class>
    <init-param>
      <param-name>javax.ws.rs.Application</param-name>
      <param-value>com.test.AddressBookApplication</param-value>
</init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>AddressBookApp</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>
In this example, the servlet mapping indicates the REST resources are served under the /app_root_context/rest directory where app_root_context is what you configured during the installation of the application. The default root context is /.

You must enable security for WebSphere® Application Server.

About this task

You can use the web container to apply authentication as well as authorization constraints to a REST application running in the application server environment. Authentication is a basic security requirement for business REST resources that require a minimum level of security and might need to further protect resources based on the identity of the caller.

You can configure the following security mechanisms for REST resources:
  • Require that users authenticate to your application using either HTTP basic authentication or form login.
  • Configure your application to use an SSL channel for transport when invoking REST resources.
  • Define role-based authorization constraints on your REST resource patterns.
  • Implement the programmatic use of the annotated SecurityContext object to determine user identity and roles.

Procedure

  1. Ensure that security is enabled for the application server.
    1. Start the WebSphere Application Server administrative console.

      Start the deployment manager, and in your browser, type the address of your WebSphere Application Server Network Deployment server. By default, the console is located at http://your_host.your_domain:9060/ibm/console.

      If security is currently disabled, you are prompted for a user ID. Log in with any user ID. However, if security is currently enabled, you are prompted for both a user ID and a password. Log in with a predefined administrative user ID and password.

    2. Click Security > Global security.

      Select Enable application security.

      Avoid trouble: You must enable administrative security. You can only have application security enabled when administrative security is enabled.
  2. Add security constraints.
    Edit the web.xml file for the application, or use an assembly tool to add security constraints to your application. The following code snippet is a security constraint applied to the AddressBookApp Sample application:
    <!-- Security constraint for the sample application -->
      <security-constraint id="SecurityConstraint_1">
        <!-- This defines the REST resource associated with the constraint. -->
        <web-resource-collection id="WebResourceCollection_1">
          <web-resource-name>AddressBookApp</web-resource-name>
          <description>Protection area for Rest resource /addresses </description>
          <url-pattern>/rest/addresses</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
        </web-resource-collection>
    
        <!—This defines an authorization constraint by requiring Role1 for the resource. -->
        <auth-constraint id="AuthConstraint_1">
          <description>Used to guard resources under this url-pattern </description>
          <role-name>Role1</role-name>
        </auth-constraint>
      </security-constraint>

    In this example, there is a web resource located at /root_context/rest/addresses that can respond to an HTTP GET or POST request. A security constraint, AuthConstraint_1, is applied to the web resource. The authorization constraint specifies that role Role1 is required for users to access the resource.

  3. Choose one or more of the following security mechanisms to configure for your REST application.
    • Enable basic HTTP authentication.
      1. Add security constraints by editing the web.xml file as previously described.
      2. Configure the web.xml file to enable basic HTTP authentication.
        Edit the web.xml file for the application and add the following element to specify the use of basic HTTP authentication. By default, the application server security runtime environment uses this method of authentication.
        <!-- This defines a HTTP basic authentication login configuration. -->
         <login-config>
                <auth-method>BASIC</auth-method>
                <realm-name>test realm</realm-name>
         </login-config>
        An HTTP basic authentication method is now defined. Users attempting to access the resource are required to login with credentials.
    • Enable form login.
      1. Add security constraints by editing the web.xml file as previously described.
      2. Edit the web.xml file for the application and add the following element to specify the use of form login:
        <login-config>
              <auth-method>FORM</auth-method>
              <form-login-config>
                 <form-login-page>/logon.jsp</form-login-page>
                 <form-error-page>/logonError.jsp</form-error-page>
              </form-login-config>
        </login-config>
        It is important that you replace the logon.jsp and logonError.jsp web page values with your form login and error processing, respectively. When accessing the application, users are redirected through the logon.jsp web page to authenticate. If there is an authentication failure, users are redirected to the logonError.jsp web page. The following example illustrates the placement of logon.jsp and logonError.jsp pages in the application web application archive (WAR) file:
        META-INF    
              logon.jsp
              logonError.jsp
              WEB-INF/classes/
              WEB-INF/classes/
              WEB-INF/classes/com/
              WEB-INF/classes/com/test/   
              WEB- NF/classes/com/test/AddressBookApplication.class
              WEB-INF/classes/com/test/AddressBookResource.class
        The following code snippet illustrates a sample logon form:
        <html>
        <head>
            <title>Login Page</title>
        </head>
        <h2>Hello, please log in:</h2>
        <br><br>
        <form action="j_security_check" method=post>
            <p><strong>Please Enter Your User Name: </strong>
            <input type="text" name="j_username" size="25">
            <p><p><strong>Please Enter Your Password: </strong>
            <input type="password" size="15" name="j_password">
            <p><p>
            <input type="submit" value="Submit">
            <input type="reset" value="Reset">
        </form>
        </html>
    • Enable SSL for your application.
      1. Add security constraints by editing the web.xml file as previously described.
      2. Edit the web.xml file for the application, and add the following element within the security-constraint element:
        <user-data-constraint id="UserDataConstraint_1">
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
         </user-data-constraint>
        
        If you do not want to use SSL, you can either skip this constraint or replace the CONFIDENTIAL value with NONE.
    • Enable authorization control to protect resources using URL patterns.
      1. Add security constraints by editing the web.xml file as previously described.
      2. Edit the web.xml file for the application and add the following element within the security-constraint element. In the following example, Role1 and Role2 specify to protect the REST resources, /rest/addresses and /rest/resources/{i}, respectively:
        <security-constraint id="SecurityConstraint_1">
            <web-resource-collection id="WebResourceCollection_1">
              <web-resource-name>AddressBookApp</web-resource-name>
              <description>Protection area for Rest Servlet</description>
              <url-pattern>/rest/addresses</url-pattern>
              <http-method>GET</http-method>
              <http-method>POST</http-method>
            </web-resource-collection>
            <auth-constraint id="AuthConstraint_1">
                 <description> Role1 for this rest resource </description>
                 <role-name>Role1</role-name>
              </auth-constraint> 
        </security-constraint>
        
        <security-constraint id="SecurityConstraint_2">
            <web-resource-collection id="WebResourceCollection_2">
              <web-resource-name>AddressBookApp</web-resource-name>
              <description>Protection area for Rest Servlet</description>
              <url-pattern>/rest/addresses/*</url-pattern>
              <http-method>GET</http-method>
              <http-method>POST</http-method>
            </web-resource-collection>
            <auth-constraint id="AuthConstraint_2">
                 <description> Role2 for this rest resource </description>
                 <role-name>Role2</role-name>
              </auth-constraint>
        </security-constraint>

        In this example, only users that are members of Role1 are able to access root-context/rest/addresses and only users that are members of Role2 are able to access the resource, root-context/rest/addresses/{i}.

        Avoid trouble: It is important that you prefix the path of the protected resources with your servlet mapping in the security constraints that you define. To prevent bypassing any access checks, you can choose to map the servlet to the /* path. This mapping protects all resources under the root context.
        Make sure to define your roles by inserting the role definition elements within the <web-app> element; for example:
        <security-role id="SecurityRole_1">
            <description>This is Role1</description>
            <role-name>Role1</role-name>
        </security-role>  
        <security-role id="SecurityRole_2">
            <description>This is Role2</description>
            <role-name>Role2</role-name>
        </security-role>  

        The changes you make to the deployment descriptor are automatically picked up by the application server runtime environment, and you do not need to restart the application or the server. Other types of changes, such as the mapping URL, require that you restart the application server. It is recommended that you restart your application to make sure that your changes take effect.

    • Programmatically using the annotated security context.
      Application developers can use the JAX-RS @SecurityContext annotation to programmatically cascade the security context down to the resource on the server side and enable the definition of security attributes during run time. The following is the functionality provided by the SecurityContext interface:
      public String getAuthenticationScheme()
      public Principal getUserPrincipal()
      public boolean isUserInRole(String role)
      The following example illustrates the SecurityContext interface:
      package com.test;
      
      import javax.ws.rs.GET; 
      import javax.ws.rs.Consumes;
      import javax.ws.rs.POST;
      import javax.ws.rs.Path;
      import javax.ws.rs.PathParam;
      import javax.ws.rs.Produces;
      import javax.ws.rs.ext.*;
      import javax.ws.rs.core.SecurityContext;
      import javax.ws.rs.core.Context;
      
      /**   
       * A sample resource that provides access to an address book. 
       * 
       */
      @Path(value="/addresses")
      public class AddressBookResource {
      	
      	@Context private SecurityContext securityContext;
          
          private static String[] list = new String[] {
              "Michael",
              "Ron",
              "Jane",
              "Sam"
          };
          
          @GET
          @Produces(value="text/plain")
          public String getList() {
             // retrieve the authentication scheme that was used(e.g. BASIC)
             String authnScheme = securityContext.getAuthenticationScheme());
             // retrieve the name of the Principal that invoked the resource
             String username = securityContext.getUserPrincipal().getName());
             // check if the current user is in Role1 
              Boolean isUserInRole = securityContext.isUserInRole("Role1");
          	 
              StringBuffer buffer = new StringBuffer();
              buffer.append("{");
              for (int i = 0; i < list.length; ++i) {
                  if (i != 0) 
                      buffer.append(", ");
                  buffer.append(list[i]);
              }
              buffer.append("}");
              
              return buffer.toString();
          }
      }
    • Use the security client handler to perform basic HTTP authentication
      You can optionally use the security client handler to perform basic HTTP authentication with a secure JAX-RS resource. The following example illustrates the simple programming model to accomplish this task:
      /**
       * This snippet illustrates the use of the JAX-RS SecurityHandler by a
       * client to perform HTTP basic authentication with a target service.
       */ 
       
       import org.apache.wink.client.ClientConfig;
       import org.apache.wink.client.Resource;
       import org.apache.wink.client.RestClient;
       import org.apache.wink.client.handlers.BasicAuthSecurityHandler;
      
       ClientConfig config = new ClientConfig();
        BasicAuthSecurityHandler secHandler = new    
       BasicAuthSecurityHandler();
      
       // Set the user credential.
       secHandler.setUsername("user1");
       secHandler.setPassword("security");
      
       // Add this security handler to the handlers chain.
       config.handlers(secHandler);
      
       // Create the REST client instance. 
       RestClient client = new RestClient(config);
      
       // Create the resource instance to interact with 
       // substitute for your resource address
       resource =  
        client.resource("http://localhost:8080/path/to/resource");
      
      // Now you are ready to call your resource.
      When using the BasicAuthSecurityHandler class, ensure that you target resources using the https scheme for your URLs, and that the target application is SSL-enabled. It is highly recommended to use SSL connections when sending user credentials. You may explicitly turn off the requirement for SSL in the BasicAuthSecurityHandler class by invoking the setSSLRequired method on the security handler with the false value. By default, this value is true.
      secHandler.setSSLRequired(false);
      Optionally, you can also provide the user credentials on the Java™ command-line for your client as follows:
      java -Duser=test_user -Dpassword=your_password  your_client_program
      You can optionally retrieve the user credentials from a properties files whose location you specify on the Java command-line as follows:
      java -Dclientpropsdir=directory_for_your_properties_file  your_client_program
      where directory_for_your_properties_file contains the wink.client.props file where the user and password properties are set.

Results

After you define security constraints, access to the REST resources that are defined in your application is subject to successful user authentication only. Additionally, you have applied role constraints to various resource URL patterns to enable role-based access to those resources.

Example

The following example illustrates the web.xml deployment descriptor for the AddressBookApp Sample application where security constraints have been defined using the previous procedure steps:
<web-app id="WebApp_1255468655347">
    <display-name>Sample REST Web Application</display-name>
    <servlet>
        <servlet-name>AddressBookApp</servlet-name>
        <servlet-class>com.ibm.websphere.jaxrs.server.IBMRestServlet</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.test.AddressBookApplication</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>AddressBookApp</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    <security-constraint id="SecurityConstraint_1">
      <web-resource-collection id="WebResourceCollection_1">
        <web-resource-name>AddressBookApp</web-resource-name>
        <description>Protection area for Rest Servlet</description>
        <url-pattern>/rest/addresses</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint id="AuthConstraint_1">
         <description>Role1 for this rest servlet</description>
         <role-name>Role1</role-name>
      </auth-constraint> 
      <user-data-constraint id="UserDataConstraint_1">
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
      </user-data-constraint>
    </security-constraint>
    <security-constraint id="SecurityConstraint_2">
      <web-resource-collection id="WebResourceCollection_2">
        <web-resource-name>AddressBookApp</web-resource-name>
        <description>Protection area for Rest Servlet</description>
        <url-pattern>/rest/addresses/*</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint id="AuthConstraint_2">
         <description>Role2 for this rest servlet</description>
         <role-name>Role2</role-name>
      </auth-constraint> 
      <user-data-constraint id="UserDataConstraint_1">
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
      </user-data-constraint>
    </security-constraint>
    <security-role id="SecurityRole_1">
         <description>This is Role1</description>
         <role-name>Role1</role-name>
    </security-role>  
    <security-role id="SecurityRole_2">
         <description>This is Role2</description>
         <role-name>Role2</role-name>
    </security-role>  
    <login-config>
      <auth-method>FORM</auth-method>
      <form-login-config>
         <form-login-page>/logon.jsp</form-login-page>
         <form-error-page>/logonError.jsp</form-error-page>
      </form-login-config>
    </login-config>
</web-app>

What to do next

Use the administrative console to administer security for your JAX-RS application.