This blog is a continuation of my previous one, Securing Java-based Web Applications: Authentication, in which I have explained the concept of Authentication with an implemented example of a Java-based Web application deployed using the Tomcat Web server.
I will now cover the concept of Authorization in the same way with an implementation example in this blog. I will also cover the definition of ‘Authorization’ with some code samples along with their explanation. The code samples provided here are in line with the code samples covered in my earlier post.
What is Authorization?
Authorization is the process of defining access policies for the resources we wish to protect. It is the way to ensure that the user has the rights to enter into the restricted area or perform a specific restricted function. In case of a Web application, it is the process of verifying the user’s rights based on his predefined role to access any restricted resource on the system.
In my previous post, I have covered the relationship between the Apache Tomcat Web Server and the Security Realm. I have also explained how to modify the $CATALINA_HOME/conf/tomcat-users.xml file to define users, roles and the mapping between the users and their roles. Additionally, I have also explained how to modify the web.xml file of an application to authenticate the user. But what happens after authentication?
Once the user has been authenticated, a Web container checks if the user is allowed to access the resource that he is requesting. It further checks if the user is allowed to perform the requested operation on that resource. This process is called Authorization. Remember however, that to enable authorization, it is necessary to enable authentication for an application by using the <login-config> setting as explained in my earlier post.
How to Implement Authorization?
It is the job of the deployer to decide which resources should be constrained and which users should be allowed to access those constrained resources. The deployer also defines the kind of operations that can be performed on those constrained resources by a user having a particular role.
The first step towards the implementation of authentication is by defining roles, which can be done by modifying the $CATALINA_HOME/conf/tomcat-users.xml file as explained in my earlier post (which also explains how to define the Manager and Guest roles).
The second step is to define the security roles in web.xml so that Tomcat can map the security roles of an application to the roles defined in the memory realm. If we are using the same names for the roles in an application as defined in tomcat-users.xml, then we just need to map the roles as illustrated below:
<web-app …> … <security-role> <description>This is a Manager role</description> <role-name>Manager</role-name> </security-role> <security-role> <description>This is a Guest role</description> <role-name>Guest</role-name> </security-role> </web-app>
If we are using different names for the roles in the servlets from the roles defined for an application, then we have to map the servlet-specific (also known as user-defined) roles to the roles defined for the application. For example, assume that we have a servlet called DeleteUserServlet in our application and a developer of that servlet has used ‘Admin’ as a role in the request.isUserInRole(“Admin”) method. At the time of development, the developer had originally thought that ‘Admin’ would be the name of the role defined for the application, but a deployer has come up with a different name for the role – ‘Manager’. In this case, a deployer can map the user-defined role with the application specific roles as demonstrated in the following code snippet.
<web-app …> … <servlet> … <security-role-ref> <role-name>Admin</role-name> <role-link>Manager</role-link> </security-role-ref> </servlet> <security-role> <description>This is a Manager role which is equivalent to Admin role</description> <role-name>Manager</role-name> </security-role> <security-role> <description>This is a Guest role</description> <role-name>Guest</role-name> </security-role> </web-app>
The last step is to define the resource and HTTP method constraints for an application in the web.xml file. Assume that we are developing a shopping cart application and we have DiscardBidServlet, DiscardItemServlet, and AddItemServlet. We will be mapping these servlets in the web.xml file as below:
<web-app …> … <servlet> <servlet-name>DiscardBidServlet</servlet-name> <servlet-class>app.admin.DiscardBidServlet</servlet-name> </servlet> <servlet> <servlet-name>DiscardItemServlet</servlet-name> <servlet-class>app.admin.DiscardItemServlet</servlet-name> </servlet> <servlet> <servlet-name>AddItemServlet</servlet-name> <servlet-class>app.common.AddItemServlet</servlet-name> </servlet> <servlet-mapping> <servlet-name>DiscardBidServlet</servlet-name> <url-pattern>/ShoppingCartApp/manage/deleteBid</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>DiscardItemServlet</servlet-name> <url-pattern>/ShoppingCartApp/manage/deleteItem</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>AddItemServlet</servlet-name> <url-pattern>/ShoppingCartApp/addItem</url-pattern> </servlet-mapping> </web-app>
Now, we would like to apply the constraint that DiscardBidServlet and DiscardItemServlet should only be accessible by a user with the ‘Manager’ role and AddItemServlet should be executed only by a user with a ‘Guest’ role. This can be achieved as described below.
<web-app …> … <security-constraint> <web-resource-collection> <web-resource-name>ManagerConstraints</web-resource-name> <url-pattern>/ShoppingCartApp/manage/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>Manager</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>GuestConstraints</web-resource-name> <url-pattern>/ShoppingCartApp/*</url-pattern> <http-method>*</http-method> </web-resource-collection> <auth-constraint> <role-name>Guest</role-name> </auth-constraint> </security-constraint> </web-app>
In the above code snippet, we have declared the URL with the pattern /ShoppingCartApp/manage/* as a constrained resource. We have further fine-tuned the constraint only for the GET and POST HTTP methods. It means that whenever a Web container receives a request for a URL that contains the above pattern, if the request is for GET or POST HTTP methods, then it will first check if the user is authenticated.
Authentication for an application is explained in my previous post. The container will further check if the user who has requested this URL has the role of a ‘Manager’ by checking the user-role mapping in the memory realm built using the tomcat-users.xml file. The servlet will be allowed to serve the request only when the above details are verified, and not otherwise.
In the above configuration, the URL pattern and HTTP methods together define which resource requests are constrained to be accessed only by the roles that are defined in the <auth-constraint> entry. Since only the ‘Manager’ role is specified in the first <security-constraint> entry, any user who does not have the role of a ‘Manager’ will not be allowed to execute GET and POST requests on the URL pattern /ShoppingCartApp/manage/*.
In the second <security-constraint> entry, we have constrained the URL pattern /ShoppingCartApp/* for all HTTP methods by specifying the ‘*’ wildcard character for the user with a ‘Guest’ role. This means that only users with ‘Guest’ roles will be allowed to access all the HTTP methods on the URLs with /ShoppingCartApp/*. Remember however, that the first <security-constraint> entry still restricts ‘Guest’ users to access ‘Manager’ specific URLs.
The <url-pattern> entry in <web-resource-collection> is mandatory; however, it is possible to specify more than one <url-pattern> in a single <web-resource-collection>. This way, we can constrain multiple URLs for the same set of roles for the same set of HTTP methods.
The <http-method> entry is optional. If omitted, all the HTTP methods will be constrained, which means that only the roles specified in the <auth-constraint> entry can access any of the methods. If used, only the specified HTTP methods will be constrained, and not the rest.
More than one <web-resource-collection> can be specified in the same <security-constraint>. This way, we can constrain multiple URLs for multiple HTTP methods for the same set of roles.
The <role-name> element within the <auth-constraint> element is optional. If it exists with specific role names, only those roles will be allowed to access the constrained resources. If the wildcard character ‘*’ is used for <role-name>, then all the users are allowed to access the constrained resources. If the <role-name> entry is omitted from the <auth-constraint> element, none of the users will be able to access the constrained resources.
The <auth-constraint> element within the <security-constraint> element is optional. If it exists, the container must perform authentication and authorization for the constrained resources. If omitted, the container must allow unauthenticated access to the constrained resources.
This concludes the implementation of Authentication and Authorization for a Java-based Web application. In my upcoming blog, I shall be discussing how to use servlet filters to deal with concerns (also known as Aspects) such as Logging and Security in an application.