Technical Article
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 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. 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
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:
- 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:In this example, the@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; } }
hello()
method is accessible by everyone, and thebye()
method is accessible by users of role javaee. - 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)
- 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 thehello
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 { ... } } }
- In web modules, you can specify
@DeclareRoles
in the servlet, filter, and tag libraries. Annotations are not supported in JSP pages. - 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
- More than one of
@DenyAll
,@PermitAll
,@RolesAllowed
cannot apply to the same method. For example, the following usage is not valid:See section 2.11 of the JSR 250 specification for details.@PermitAll @DenyAll public String hello()
- Two
@RolesAllowed
annotations cannot apply to the same methods. For example, the following usage is not valid and will fail in compilation.To achieve the desired effect, provide authorized roles to the@RolesAllowed("javaee") @RolesAllowed("j2ee") public String hello()
@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 |
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. |
---|
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>
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:
- Create your Java EE application.
- Set up authorization constraints using security annotations for Enterprise JavaBeans, or add a security constraint to the web module's web.xml file.
- Set up authentication requirements
<login-config>
in the corresponding deployment descriptor. - Set up the realm (if you are not using the default realm) or run-as principal in the applicable deployment descriptors.
- Map application roles to application server groups or principals by adding the
<security-role-mapping>
element to the runtime deployment descriptors. - Package and deploy your application.
References
- JSR 250: Common Annotations for the Java Platform
- 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.