Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Servlet security with Spring AOP and annotations: Authorization

In this article, we will see a simple technique for a declarative model of authorization. We will use Java class-level annotations to describe the authorization requirements for each controller in the application.

Introduction

This article continues from where my last article, Servlet security with Spring AOP and annotations: Authentication, left off. If you haven’t read that article yet, you should do so now, as it contains the code for authentication that this article is based on. It also includes a list of other suggested articles.

The process of authorization is the means by which we decide whether any particular user (whether they are logged in or not) is allowed to perform any particular action in our application. For simple web applications, we usually make these restrictions at the level of particular Controllers, as defined by the Model-View-Controller pattern. In some circumstances we may also add further restrictions, such as limiting a client account manager’s access to only records that pertain to clients who he is the manager for, or when implementing functions that clients are expected to be able to use we obviously must restrict those functions to only the data relating to the client who has logged in.

We must therefore check each request prior to execution to ensure it is acceptable for the current user. The obvious way of doing this is to insert code into each Controller object to perform the validation, but doing so would generally be a mistake. The Single Responsibility Principle is widely accepted as an important design principle for object-oriented software, helping us to produce systems that are easier to maintain and more flexible. The principle states, using the phrasing suggested by Robert C. Martin, that each class should have only one reason to change, and a class that is both performing business-domain logic and authorization logic can change either if the business rules change or if the authorization requirements change, so would be in violation.

It is also true that introducing repetitive code in multiple places across an application widens the scope for bugs in that code, particularly when it is code whose purpose is tangential to the purpose for which the programmer writing the rest of the class it resides in is currently working. Programmers spend most of their time trying to enable users to perform actions; preventing users from performing actions they are not supposed to perform requires a different mindset, and it is easy to fail to adequately test authorization code. But bugs in authorization can be very serious and introduce real costs for businesses, so any steps that can be taken to reduce their likelihood are important.

I’ll show how this can be used to grant different users different access levels, and although I do not describe the changes that would be required were only a subset of records to be accessible to a user I hope that the approaches to such a requirement will become obvious.

Annotating Controllers

The first step is to define some annotations that can be used on our controller classes. I’ve decided to use just a single annotation type for now, which I’ve called RequireLogin. In larger applications I often also add a second annotation type, which I call PublicAccess. I’ll discuss the reasons I do this later, but for now we will proceed without this second type.

Listing 1: The RequireLogin annotation

 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequireLogin
{
	boolean createCustomer ()		default false;
	boolean createAccount ()    	default false;
}

The semantics should be obvious: when a controller is tagged with @RequireLogin, users must log in prior to being permitted to execute requests on that controller. Furthermore, we can specify @RequireLogin(createCustomer=true), which means that the logged in User object must have its canCreateCustomer field set to true. Similarly, @RequireLogin(createAccount=true) requires the User to have its canCreateAccount field set to true. The annotation is applied to types (in actual fact, only to classes) and is used at run time so we must ask the JRE to retain it at run time. I also tag it as documented, so the security requirements of each controller class should show up in the generated documentation for it, which may simplify security reviews.

I then add the annotation to my controllers, for example:

Listing 2: Annotating the NewAccount controller

 
@RequireLogin(createAccount=true)
public class NewAccount extends HttpServlet
{
	…
}

With the annotations in place, we can move on to intercepting the requests and interpreting the annotations.

The Security Manager

We start by adding a SecurityManager class, and add an entry into the applicationContext.xml file to create an instance. I also ensure that Spring DAO is enabled and set to work on full classes (by default it will only operate on instances of interfaces, but as HttpServlet is a class rather than an interface we cannot use it this way).

Listing 3: Creating the SecurityManager

 
    	<!-- Enable Spring AOP for classes, and scan annotations -->
<aop:config proxy-target-class="true" />
<aop:aspectj-autoproxy/>

<bean id="securityManager" class="com.example.mvc.SecurityManager" />

The SecurityManager class declares itself as an Aspect using annotations, and declares a single method to be advised when requests are executed:

Listing 4: Declaring and configuring the SecurityManager with annotations

 
@Aspect
public class SecurityManager
{
	@Around("execution(* javax.servlet.http.HttpServlet.service(..)) && "+
				"args(request,response) && "+
				"@target(annotation)")
	public void checkAccess (ProceedingJoinPoint joinPoint,
	                         HttpServletRequest request, HttpServletResponse response, 
	                         RequireLogin annotation) throws ServletException, IOException
	{
		…
	}
}

The “Around” annotation declares that we want to be called before the execution of the target method, and that we will take control of executing the target inside the context of our own method. We are provided with a ProceedingJoinPoint implementation that provides an interface for executing the target. The first line of the annotation’s value indicates the method we care about, the service() method of HttpServlet. We then specify names for the method’s two arguments, and because those names match names of arguments to our method we are passed the arguments directly (we could get them out of the ProceedingJoinPoint object if we wanted, but this way we don’t have to worry about the potentially unsafe casting operations). We also specify that the target object should have an annotation, and give it a name of “annotation”. Again, because we declare a parameter to our method with the same name it will be passed to us. This also has the effect of ensuring our method will not be called if there is no annotation of the required type, so any servlet class that we do not give a RequireLogin annotation to will not be affected by this Aspect.

Inside the checkAccess method, we need to ensure that a user is logged in and if we have requested any specific restrictions on the valid users we also have to check that the user matches those restrictions.

Listing 5: Checking the user’s authorization

 
	public void checkAccess (ProceedingJoinPoint joinPoint,
	                         HttpServletRequest request, HttpServletResponse response, 
	                         RequireLogin annotation) throws ServletException, IOException
	{
		HttpSession session = request.getSession (false);
		User currentUser = session != null ? (User)session.getAttribute ("user") : null;
		if (currentUser == null)
		{
			request.getRequestDispatcher ("/templates/login-required.jsp")
.forward (request, response);
			return;
		}
		if (annotation.createAccount () && !currentUser.canCreateAccounts ())
		{
			request.setAttribute ("requiredPermission", "create accounts");
			request.getRequestDispatcher ("/templates/permission-denied.jsp")
.forward (request, response);
			return;
		}
		if (annotation.createCustomer () && !currentUser.canCreateCustomers ())
		{
			request.setAttribute ("requiredPermission", "create customers");
			request.getRequestDispatcher ("/templates/permission-denied.jsp")
.forward (request, response);
			return;
		}
		…
	}

If there is no user attribute in the session, we are not logged in. We display a view that informs them that they have to log in before performing the action they requested. Otherwise, we check the annotation to see if it requests a specific user permission. If it does, and the user does not have the permission we need, we set an attribute in the request so the view knows what permission was required, and send back a view that informs the user that they don’t have access.

If we get to the end of the code above, we know the user is allowed to perform the action they requested, so we need to actually call it:

Listing6: Executing the original request

 
		…
		
		try
		{
			joinPoint.proceed ();
		}
		catch (ServletException|IOException|RuntimeException e)
		{
			throw e;
		}
		catch (Throwable e)
		{
			throw new RuntimeException ("Unexpected exception at join point", e);
        		}
	}

We call joinPoint.proceed() to execute the original request. This method is declared to throw any Throwable object, but we should only throw exceptions that HttpServlet.execute() is allowed to throw (which is to say, ServletException, IOException and unchecked RuntimeExceptions). We catch exceptions thrown by the target method and rethrow them if they are allowed; otherwise we wrap them in a new RuntimeException. Theoretically, we should only ever see exceptions that are allowed, but it is best to check them anyway.

In the last article, I set up 4 different users for the different combinations of permissions. You can log in using any of them (the Login servlet should not be annotated with a requirement to have logged in, of course, but all the others should be) and check them against different combinations of requirement in the servlet annotations to check the scheme is working.

There are a variety of more advanced mechanisms possible for deciding which users are permitted to perform which actions. The one I’ve shown here has the disadvantage of needing a database change every time you define a new permission, which can be cumbersome. You may instead decide to allocate your users to groups and require group membership for particular actions, or to give users an explicit list of actions that they are allowed to perform. But whatever organisational technique you decide upon, you can use this method of intercepting requests to separate the decision from the actual implementation of the actions.

One final thing to consider is whether you want to allow unannotated servlets. It is possible to declare an interceptor for calls to methods in classes that do not have a specific annotation. By requiring an annotation on all servlets, you may be able to prevent the possibility of a developer forgetting to annotate a servlet and thereby leaving some function accessible to the public without authorization or authentication.

This is all for this article. See you next time.



My main area of specialization is Java and J2EE. I have worked on many international projects like Recorders,Websites,Crawlers etc.Also i am an Oracle Certified java professional as well as DB2 certified

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login