How to : Spring Security (ex Acegi) and GWT
Par David MARTIN le jeudi 19 février 2009, 11:45 - GWT - Lien permanent
As you know, Spring Security (previously Acegi) provides an easy and robust solution to secure web applications. Thereby, web developers do not have to deal with all that stuff in their source code, but just have to configure Spring Security to enable authentication and authorization for their application.
As it works pretty good with 'conventional' application, things go less cool with applications like GWT ones. This is mainly due to the asynchronous calls.
Indeed, asynchronous calls and especially RPC, have to follow a predefined format in order to be processed the right way. But because Spring Security acts at a high level in the workflow, viz. protecting resources without them to be aware of that, some unexpected exceptions can be thrown by Spring Security before the resource is even reached.
If such an error occurs, the GWT client will receive an unexpected exception with no clear reason attached (except a "see log file" tip)... Such an error can be either a NullPointerException thrown in the middle of the remote service or a security exception thrown right before calling the service (Reasons can be no current authentication information for instance, or rights not sufficient for this particular resource).
The behavior when receiving an exception from the security stack may be different (in fact, it often should be) from any other technical exception. That's why more information is needed in that specific case in order to treat them in an appropriate way.
I've started developing this project with this need in mind. Because I've already used GWT in Spring powered applications, I'm familiar with GWT-SL, a clean (that's my POV) way to integrate Spring beans with GWT RPC needs. That's why I chose to use this library.
I also decided to enhance the AsyncCallback behavior, adding a dedicated security exception handling mechanism. That way, the new AsyncCallback entity will be able to distinguish a security error from a technical (and unexpected) error.
Considering the server side part of the work, the main idea was to provide something as light as possible for the developer. My choice was to override the GWTHandler class (that is responsible of the exposure of the Spring powered remote services) and provide a (semi)transparent security exception management. Why not a totally transparent one ? Because I didn't find a better solution for now ;-)
But, because GWT RPC must know what exceptions a service can throw (otherwise, every other exception is an unexpected one, with no clear information about it...) the developer has to add on every method of his RPC services an exception definition like this one :
{SomeObject|void} myMethod(..) throws ApplicationSecurityException;
From this point you have to make a (terrible :) ) choice : use AOP (through annotations or pointcuts) or URL filtering in order to secure your remote calls.
Both are supported : that's the good point :) But depending on what method you choose, you may have to set up the configuration slightly differently.
Let's first start with AOP, the way I definitely prefer. In order to use it, the Spring Configuration must enable its support with this declaration:
<global-method-security secured-annotations="enabled" />
That's enough to detect every @Secured annotation the developer can add to his code. Speaking about that, here is how it looks :
@Secured({"ROLE_USER"})
{SomeObject|void} myMethod(..) throws ApplicationSecurityException;
And that's it ! The modification only affects application's Remote Service interfaces (the ones who extend RemoteService).
If you do not want annotations in your source code, you can use pointcuts. Instead of the previously mentioned global-method-security declaration, you should use this one :
<global-method-security secured-annotations="disabled" jsr250-annotations="disabled">
<protect-pointcut
expression="execution(* com..MyGreatRPCServiceImpl.myMethod(..))"
access="ROLE_USER" />
</global-method-security>
Everything else remains the same.
As you can see, that's really easy to set up, at least for AOP...
Now, let's figure out you want to use URL filtering instead of AOP. In this specific case, things do not work exactly the same way. In fact, that's a little bit more complex.
First and foremost, the solution I'll provide can't be used with the Spring Security 2.0 namespace... In other words, that means you have to declare every Spring Security bean the old way... That's not really fun, especially if you have discovered Spring Security recently with the 2.0 release... Well, anyway...
URL filtering checks if the user accessing a resource is allowed or not BEFORE anything else AND independently if it's a specific RPC call or anything else. That's why the solution must be smart enough to deal with this and adapt its response depending on each case !
When accessing a resource without enough privilege, an error is thrown. The ExceptionTranslationFilter bean is used to determine what is the reason of the error and how to deal with it. It means if the user isn't authenticated (or is authenticated but is ANONYMOUS), a redirection to the login page will occur, otherwise the management of the error is delegated to a handler.
I've made the choice to override the ExceptionTranslationFilter and enhance its behavior. Here is how it should work : If an exception (I mean a SpringSecurityException) occurs, the newly created (and more powerful :) ) ExceptionTranslationFilter should check if the URL that generated this exception was related to a RPC call or not. If so, the original behavior should change to something specific : either send a message for the RPC call or send something else that could be processed by the callback class. Actually, SecuredAsyncCallback can manage, besides classic RPC responses, XML responses that follow this pattern :
<security> <code>XXX</code> <message>XXX</message> <exception>XXX</exception> </security>
In order to determine if a request comes from a RPC call or something else, the new ExceptionTranslationFilter - called GWTExceptionTranslationFilter - must rely on pattern analysis. Thus, if a request matches one of the patterns provided, the answer will be different from the original one.
Let's focus on the two specific sample declarations related to URL Filtering for GWT :
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="objectDefinitionSource">
<filter-invocation-definition-source>
<intercept-url pattern="/rpc/**" access="ROLE_USER" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</filter-invocation-definition-source>
</beans:property>
</beans:bean>
<util:set id="gwtPathsSet" set-class="java.util.HashSet" value-type="java.lang.String">
<beans:value>/rpc/**</beans:value>
</util:set>
<beans:bean id="exceptionTranslationFilter"
class="com.gwtincubator.security.server.GWTExceptionTranslationFilter">
<beans:property name="authenticationEntryPoint"
ref="authenticationProcessingFilterEntryPoint" />
<beans:property name="gwtPaths" ref="gwtPathsSet" />
</beans:bean>
First, the filterSecurityInterceptor should describe the URL you want to filter with patterns.
Last, The GWTExceptionTranslationFilter should have a set of patterns in order to distinguish asynchronous calls to others calls.
And that's it (presuming you know how to declare all other beans with the namespace...)
I hope this quick tutorial (and the source code samples) will help some of you to integrate Spring Security to your application, or at least help you understand how a GWT application can be secured with Spring Security.
Feel free to post comments, read the whole documentation and download the code.
NOTE : There may be some mistakes here and there as English is not my mother tongue ;)
Useful links :


Commentaires
Bonjour,
article très intéressant dont j'aurais l'usage ultérieurement.
A quand la disponibilité automatique des articles de ce blog en pdf (la MEP n'est pas conservée avec pdfcreator) ?
Dois-t'on écrire mozer tong ou primairie langouaidje ?
Fenkseulotte
Wad
Merci pour le retour.
PDF redirect (gratuit) conserve très bien la mise en page.
La "mother tongue" n'est pas forcement toujours identique à la notion de "primary language" : l'un et l'autre peuvent etre différents. Et dans mon cas, l'anglais n'est ni l'un ni l'autre :) donc les deux pouvaient convenir.
David