Security Annotations and Authorization in GlassFish and the Java EE 5 SDK

   
By Shing Wai Chan, July 2006  

Security is very important in the enterprise environment. In the Java EE 5 / GlassFish environment, you can achieve security using the following options:

  • Transport Level Security (TLS) / Secure Sockets Layer (SSL) technologies
  • Authentication and Authorization
  • Message Level Security (for Web Services in GlassFish only)

This article discusses authentication and authorization. References [ 1], [ 2], and [ 3] discuss how to set up the SSL environment in both clients and servers for Enterprise JavaBeans and Web Services. Message level security for Web Services will be discussed in a future article.

Authentication verifies the identity of a given user, typically by requiring the user to enter a username and password. In the Java EE environment, authentication is associated with a realm. The realm can store user identity information in many ways, including files, LDAP directories, and even databases accessed through JDBC (see Reference [4]). It can also work with Solaris PAM (Pluggable Authentication Modules) framework.

Authorization grants access control permissions based not only on what software is running but also on identity of the authenticated user who is running it. Each time a user logs in, he or she is granted a set of permissions for each application.

Prior to Java EE 5, if you wanted to use authorization for a given application, you needed to specify authorization information in the application deployment descriptors ejb-jar.xml or web.xml. One of the main focuses of Java EE 5 is to simplify development of Java EE applications. Starting in Java EE 5, developers can specify annotations in Java source files instead of putting metadata in deployment descriptors. Annotations simplify the development of Java EE applications, shortening development cycles and reducing the total cost of ownership.

JSR 250 (see Reference [5]) defines common annotations for the Java platform. This article discusses the security annotations defined in JSR 250 and demonstrates how to use them for securing an application with authentication and authorization.

 
Contents
 
Basic Definitions and Examples
Examples of Invalid Use of Annotations
Inheritance of Security Annotations
Using Deployment Descriptors
Conclusion
References
Acknowledgments
 
Basic Definitions and Examples

An annotation is a special kind of modifier, and can be used anywhere that other modifiers can be used. Annotations consist of an at-sign ( @) followed by an annotation type and a parenthesized list of element-value pairs.

This section discusses the common security annotations defined in JSR 250. Five security annotations are defined (see reference [6]):

  • javax.annotation.security.PermitAll
  • javax.annotation.security.DenyAll
  • javax.annotation.security.RolesAllowed
  • javax.annotation.security.DeclareRoles
  • javax.annotation.security.RunAs

The annotations @PermitAll, @DenyAll and @RolesAllowed are defined for specifying permissions of EJB business methods. @DeclareRoles and @RunAs are TYPE-level annotations defined for roles-related metadata.

For a web module, you still need to specify a <security-constraint> in the web.xml application deployment descriptor in order to have authorization constraints, just as you did in J2EE 1.4. In the Java EE 5 environment, the permissions-related annotations are only defined for EJB modules. The following table summarizes the basic usage of these annotations. More details can be found in the JSR 250 specification ( reference [5]).

Annotations

Target

EJB or its super classes

Servlet or web libraries

Descriptions

TYPE

METHOD

@PermitAll

X

X

X

 

Indicates that the given method or all business methods of the given EJB are accessible by everyone.

@DenyAll

 

X

X

 

Indicates that the given method in the EJB cannot be accessed by anyone.

@RolesAllowed

X

X

X

 

Indicates that the given method or all business methods in the EJB can be accessed by users associated with the list of roles.

@DeclareRoles

X

 

X

X

Defines roles for security checking. To be used by EJBContext.isCallerInRole, HttpServletRequest.isUserInRole, and WebServiceContext.isUserInRole.

@RunAs

X

 

X (not for non-EJB super classes)

X (for servlet only)

Specifies the run-as role for the given components.

 


Notes:

  1. For the annotations @PermitAll, @DenyAll, and @RolesAllowed, a class-level annotation applies to a class and a method-level annotation applies to a method. Method-level annotation overrides the behavior of class level annotation.

    Example: Consider the following code:

            @Stateless
            @RolesAllowed("javaee")
            public class HelloEJB implements Hello {
                    @PermitAll
                    public String hello(String msg) {
                            return "Hello, " + msg;
                    }
    
                    public String bye(String msg) {
                            return "Bye, " + msg;
                    }
            }
    
     
    In this example, the hello() method is accessible by everyone, and the bye() method is accessible by users of role javaee.
  2. The @DeclareRoles annotation defines a list of roles to be used by a given component. In the Java EE 5 environment, you can look up the resource by using the @javax.annotation.Resource and verify whether the user is of the given role by invoking the following APIs:

    Component

    API to check role

    EJB

    javax.ejb.EJBContext.isCallerInRole( role)

    Servlet

    javax.servlet.http.HttpServletRequest.isUserInRole( role)

    Web Service

    javax.xml.ws.WebServiceContext.isUserInRole( role)

  3. Although the @PermitAll, @DenyAll, and @RolesAllowed annotations allow you to implement most of the authorization decision, you need the @DeclareRoles annotation to help you to accomplish more complicated logic.

    Example: Suppose the hello method is to be accessible by a user who is in role A and who is not in role B at the same time. The following code clip achieves this goal:

            @Stateless
            @DeclaresRoles({"A", "B"})
            public class HelloEJB implements Hello {
                    @Resource private SessionContext sc;
                    public String hello(String msg) {
                            if (sc.isCallerInRole("A") && !sc.isCallerInRole("B")) {
                                    ...
                            } else {
                                    ...
                            }
                    }
            }
    
     
  4. In web modules, you can specify @DeclareRoles in the servlet, filter, and tag libraries. Annotations are not supported in JSP pages.
  5. The relationship between the run-as or caller identity and the client identity used in an embedded web service invocation is not defined. This lack of definition means that you cannot assume that the run-as identity or caller identity will be propagated as the client identity on the web service invocation.
Examples of Invalid Use of Annotations
  1. More than one of @DenyAll, @PermitAll, @RolesAllowed cannot apply to the same method.
    For example, the following usage is not valid:

            @PermitAll
            @DenyAll
            public String hello()
    
     
    See section 2.11 of the JSR 250 specification for details.
  2. Two @RolesAllowed annotations cannot apply to the same methods.

    For example, the following usage is not valid and will fail in compilation.

            @RolesAllowed("javaee")
            @RolesAllowed("j2ee")
            public String hello()
    
     
    To achieve the desired effect, provide authorized roles to the @RolesAllowed annotation as a list, as shown in this example:

            @RolesAllowed({"javaee", "j2ee"})
            public String hello()
    
     
Inheritance of Security Annotations

This section discusses the inheritance of security annotations. Because the default behavior of methods in GlassFish is @PermitAll, this annotation is omitted in the discussion below for simplicity.

The general rule of inheritance is as follows:

  • For methods, apply annotations that are associated with the method chosen in inheritance hierarchies.
  • For the @RunAs annotation, only those in the leaves of the hierarchy will be considered.
  • For the @DeclareRoles annotation, inheritance is additive from the inheritance hierarchies.

The following examples illustrate these rules of inheritance of annotations.

Example: In the following hierarchies of EJBs:


The following method permissions apply:

Methods of Hello

Method Permissions of HelloBaseEJB

Method Permissions of HelloEJB

hello1()

accessible by user of role “manager”

accessible by everyone

hello2()

accessible by user of role “employee”

accessible by user of role “staff”

hello3()

accessible by user of role “employee”

accessible by user of role “employee”



Example: In the following hierarchies of Servlets:


Servlet

Roles Defined

RunAs

HelloBaseServlet

employee

engineer

HelloServlet

employee, manager

staff

HelloServlet2

employee

 

Note that the run-as role is not set for HelloServlet2.

Using Deployment Descriptors

By using annotations, the deployment descriptors of an application are simplified. However, there are some scenarios in which we still need or prefer to use deployment descriptors. These scenarios are described in this section.

  1. For EJB web service endpoints with @RolesAllowed, you need to specify the type of authentication to use by specifying the <login-config> and <auth-method> elements in sun-ejb-jar.xml. For username-password authentication, set the <auth-method> element to BASIC, as shown in the following example. This step is required only for EJB web service endpoints, and is not required for EJBs.

    <sun-ejb-jar>
          <enterprise-beans>
                <ejb>
                      <ejb-name>HelloEjb</ejb-name>
                      <webservice-endpoint>
                            <port-component-name>HelloEjb</port-component-name>
                            <login-config>
                                  <auth-method>BASIC</auth-method>
                                  <realm>default</realm>
                            </login-config>
                      </webservice-endpoint>               
                    </ejb>
            </enterprise-beans>
    </sun-ejb-jar>
    
     
  2. The @PermitAll, @RolesAllowed, @DenyAll annotations are not supported in servlets. When using servlets, specify authentication and authorization in the web.xml deployment descriptor. The following information must be specified in web.xml:

    • <security-constraint>/<web-resource-collection>
      These elements specify the URL pattern and HTTP methods of the web resources that need to be protected.
    • <security-constraint>/<auth-constraint>
      These elements specify a list of roles that are allowed to access the protected web resources.
    • <login-config>
      This element specifies the type of authentication to be performed (for example, <auth-method> (BASIC or FORM)) , the realm in which authentication is to be performed ( <realm-name>), and, in the case of form-based authentication, the location of the login form and error page ( <form-login-config>).
    • <security-role> This element specifies a list of roles used by this web application. This list must include the roles specified in the <auth-constraint> mentioned above.

      Example: Suppose you want to protect the GET and POST methods of index.jsp so that they are only accessible by the role employee. The web.xml configuration that you would use to specify this is as follows:

              ...
              <web-app>
                      <servlet>
                              ...
                      </servlet>
                      <
                                             security-constraint>
                              <
                                             web-resource-collection>
                                      <web-resource-name>MySecureResource</web-resource-name>
                                      <url-pattern>/index.jsp</url-pattern>
                                      <http-method>GET</http-method>
                                      <http-method>POST</http-method>
                              </web-resource-collection>
                              <
                                             auth-constraint>
                                      <role-name>employee<role-name>
                              </auth-constraint>
                              <user-data-constraint>
                                      <transport-guarantee>NONE</transport-guarantee>
                              </user-data-constraint>
                      </security-constraint>
                      <login-config>
                              <auth-method>BASIC</auth-method>
                              <realm-name>default</realm-name>
                      <login-config>
                      <security-role>
                              <role-name>employee</role-name>
                      </security-role>
              </web-app>
                                          
       
  3. For all applications that restrict access to a specified role, you need to map the role used in the application to the group or principal that is defined on the applications server. To map the role of employee that is used in this application to the group of engineer that is defined for the default realm of the Application Server, you still need to specify this security role mapping in the runtime deployment descriptors sun-application.xml, sun-ejb-jar.xml, or sun-web.xml.

    For example, to map all users belonging to group engineer to the role of employee in a given application, put the following code in sun-application.xml:

            <security-role-mapping>
                    <role-name>employee</employee>
                    <group-name>engineer</group-name>
            </security-role-mapping>
    
     
  4. Deployment descriptors override behaviors in annotations.

    Example: Assume the following Enterprise JavaBean:

            @Stateless
            public class HelloEJB implements Hello {
                    @PermitAll
                    public String hello1(String msg) {
                            return "1: Hello, " + msg;
                    }
    
                    @RolesAllowed("javaee")
                    public String hello2(String msg) {
                            return "2: Hello, " + msg;
                    }
    
                    @DenyAll
                    public String hello3(String msg) {
                            return "3: Hello, " + msg;
                    }
            }       
    
     

    You can override method permissions in the application deployment descriptor ejb-jar.xml as follows:

            <ejb-jar ...>
                    <enterprise-beans>
                            ...
                    </enterprise-beans>
                    <assembly-descriptor>
                            <security-role>javaee</security-role>
                            <method-permission>
                                    <role-name>javaee</role-name>
                                    <method>
                                            <ejb-name>HelloEJB</ejb-name>
                                            <method-intf>Local</method-intf>
                                            <method-name>hello1</method-name>
                                    </method>
                            </method-permission>
                            <method-permission>
                                    <unchecked/>
                                    <method>
                                            <ejb-name>HelloEJB</ejb-name>
                                            <method-intf>Local</method-intf>
                                            <method-name>hello3</method-name>
                                    </method>
                            </method-permission>
                            <exclude-list>
                                    <method>
                                            <ejb-name>HelloEJB</ejb-name>
                                            <method-intf>Local</method-intf>
                                            <method-name>hello2</method-name>
                                    </method>
                            </exclude-list>
                    </assembly-descriptor>
            </ejb-jar>
    
     
    The behaviors are as follows:

    Method

    From Annotation

    Deployment Descriptor Overriding

    hello1()

    Accessible by everyone

    Accessible by user of role javaee

    hello2()

    Accessible by user of role javaee

    Not accessible by anyone

    hello3()

    Not accessible by anyone

    Accessible by everyone

  5. In GlassFish, if the realm is not specified in an application deployment descriptor, then the default realm is used for an application. You can override this setting using the <realm> element in the runtime deployment descriptor sun-application.xml, sun-ejb-jar.xml, or <realm-name> in web.xml.
  6. If you are using the @RunAs annotation in GlassFish, you need to associate the run-as role to a principal. If there is only one principal associated with the role, that principal will be taken as the default run-as principal value. If there is more than one principal associated with the role, you need to explicitly set the run-as principal. The following example demonstrates setting the run-as principal in sun-ejb-jar.xml:

            ...
            <ejb>
                    ...
                    <principal>
                            <name>user1</name>
                    </principal>
                    ...
            </ejb>
    
     

    And in sun-web.xml,

            ...
            <servlet>
                    <servlet-name>myServlet</servlet-name>
                    <principal-name>user1</principal-name>
                    ...
            </servlet>
            ...
    
     
Conclusion

In conclusion, annotations make it very simple to secure an application using authentication and authorization in the Java EE 5 environment. When using authentication and authorization in the Java EE 5 environment, you typically follow these steps:

  1. Create your Java EE application.
  2. Set up authorization constraints using security annotations for Enterprise JavaBeans, or add a security constraint to the web module's web.xml file.
  3. Set up authentication requirements <login-config> in the corresponding deployment descriptor.
  4. Set up the realm (if you are not using the default realm) or run-as principal in the applicable deployment descriptors.
  5. Map application roles to application server groups or principals by adding the <security-role-mapping> element to the runtime deployment descriptors.
  6. Package and deploy your application.
References
  1. How to use Verisign cert in GlassFish and SJSAS 8.x?, Shing Wai Chan's Weblog, December 16, 2005
  2. Enterprise Java Bean over SSL, Shing Wai Chan's Weblog, April 8, 2006
  3. Using JAX-WS-Based Web Services with SSL, in Enterprise Java Technologies Tech Tips for May 30, 2006, Shing Wai Chan, May 27, 2006
  4. JDBCRealm in GlassFish, Shing Wai Chan's Weblog, June 8, 2006
  5. JSR 250: Common Annotations for the Java Platform
  6. Java EE 5 SDK API Specifications: Package javax.annotation.security Descripton
Acknowledgments

Thanks to Debbie Carson and Rick Palkovic for their suggestions for improving the article.

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.
Shing Wai Chan is a senior member of the Sun Java Application Server and Java EE SDK development teams. He has been focusing on development projects that relate to security, annotations, CMP, B2B and B2C.
 

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.