When writing an Servlet for some additional processing from within a portlet (say a PDF or Excel report) it may be necessary to secure that Servlet from unauthorized access. The Spring Framework comes to the rescue with Spring Security which provides a nice declaritive approach to protecting your Servlet resources.
In this post I will detail how to use Spring Security with Spring Portlet MVC . Oddly enough, its all XML configuration; you really write no code.
First off you'll need the Spring Security libraries. We're going to use Spring Security 2.0.3 in this example. If you're using Maven 2, add the following to your pom.xml
Next, we're going to create 2 files, security-portlet.xml and security-servlet.xml.
We start with the security-servlet.xml file by adding namespace declarations like so
If you're wondering why we declared <beans:beans>, it's because we've defined the root namespace to be security. This will make things a little bit more clean throughout the file. (If we had defined the root namespace to be beans, we would have to prefix everything with whatever we defined the security namespace prefix to be, such as <security:http>)
Next we define an <http> element and tell it what pattern we want to intercept (the Servlet) and what ROLE (ROLE_ is the default prefix, you can change it though) is allowed to access that resource. The default is to use Ant style patterns, so values like /** or **/*.html are valid patterns. The <http> element is a shortcut namespace element for ease of setup and defines some defaults. Check out the documentation for more information on what these defaults are.
You'll also notice that we gave <http> an "entry-point-ref" to preAuthenticatedProcessingFilterEntryPoint. In a portal environment, the Portal is usually responsible for the authentication so by using this bean we're telling Spring that we expect to already be authenticated at this point. If we're not, it was always return HttpServletResponse.SC_FORBIDDEN
Similarly we define a preAuthenticatedProcessingFilter and a preAuthenticatedUserDetailsService because again, the portal will be responsible for already having authenticated the user and we're assuming the the portal will be providing our User Details as well (think portal ROLE's)
One small note here is that when we defined the <http> element we already defined an authenication manager (this is one of the defaults I eluded too earlier) so we have to tell spring with the <custom-authentication-provider> tag that we are using another authentication provider.
Lastly we create an alias to the authentication manager because later on we'll need to use this reference within other bean definitions
Before we move on to our security-portlet.xml, we need to add a filter to the web.xml so our requests will be dispatched to our beans defined in security-servlet.xml. Add the following to the web.xml
Now we start with the security-portlet.xml. First we declare the namespaces
Next, we need to declare a PortletProcessingInterceptor so we can inject the authenticationManager (remember the alias we created?) and a PortletPreAuthenticatedAuthenticationDetailsSource which we'll define in a second
Next we create the PortletPreAuthenticatedAuthenticationDetailsSource. Remember how we said earlier that the portal would be responsible for providing information for User Details. Well this is where we define what roles of the portlet we want to map. Essentially this will call isUserInRole() for each value in the list.
Latestly, define PortletSessionContextIntegrationInterceptor to keep the authentication information in the session so it isn't lost on every request.
If you're wondering where PORTAL_ADMINISTRATORS came from above, we define that value in the portlet.xml
The role-link value is usually specific to the portal environment you're using so check with your portal on how to do roles.
We're almost finished. We need to add the interceptors that we defined earlier to the portletModeParameterHandlerMapping (or portletModeHandlerMapping) so that they are applied prior to mapping our handlers.
I should note the order of the two interceptor's we added is important. The portletSessionContextIntegrationInterceptor always needs to come first otherwise we will be doing the whole authentication process for every request.
Lastly, we need to add our context files to the root context by configuring the web.xml
A final important note is that the portlet and the servlet need to share session data. If you're using Tomcat, you can add the following within your <connector> definition in server.xml. See here if you want a more in depth explanation.
And that's it! Now, every request through the portal to /excelReport will require ROLE_PORTAL_ADMINISTRATOR otherwise they will recieve a 403 unauthorized message. Additionally, any request directly too /excelReport without going through the portal will also recieve a 403 unauthorized message!
Feel free to post any questions or comments you might have.