This solution utilizes AOP, and I will use aspectJ for compile-time weaving but you can use spring-aop if there are reasons not to use aspectJ in your project.
Let's start with security exception that will work on GWT client side.
package com.mm.client; import java.io.Serializable; public class AppSecurityException extends RuntimeException implements Serializable { public AppSecurityException() { super(); } }There are 2 things to note: this exception should be unchecked exception and you should specify it in throws declaration of secured GWT RPC method despite it is unchecked exception.
I'm leaving up to you adding reasons why exception is thrown (Not logged in, not authorized, session expired etc.)
Next, create Spring context configuration that will configure Spring Security to use method level security only, without URL filtering because you don't need it in GWT RPC.
I also including here configuration for annotation-driven context and aspectJ configuration.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <context:annotation-config/> <context:spring-configured/> <context:component-scan base-package="com.mm"/> <security:global-method-security secured-annotations="enabled" mode="aspectj"/> <bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy"> <security:filter-chain-map path-type="ant"> <security:filter-chain pattern="/app/*" filters="securityContextPersistenceFilter"/> </security:filter-chain-map> </bean> <bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"> <property name="forceEagerSessionCreation" value="true"/> </bean> </beans>Do not forget to include filter in web.xml
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/app/*</url-pattern> </filter-mapping>Next step is to define marker annotation for secured methods
package com.mm.server; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface GwtMethod { }And last part of setup is to define aspect that will translate Spring Security exception to your app exception:
package com.mm.server; import com.mm.client.AppSecurityException; import org.springframework.security.core.AuthenticationException; public aspect SecurityExceptionAspect { declare precedence:SecurityExceptionAspect,*; private pointcut executionOfGwtMethod(): execution(* *(..)) && @annotation(GwtMethod); Object around(): executionOfGwtMethod() { try { return proceed(); } catch (AuthenticationException e) { throw new AppSecurityException(); } } }That's all.
You can use this in following way:
package com.mm.server; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.mm.client.AppSecurityException; import com.mm.client.SecurityTestService; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.security.access.annotation.Secured; @Configurable public class SecurityTestServiceImpl extends RemoteServiceServlet implements SecurityTestService { @Override @Secured("ROLE_LOGGED_IN") @GwtMethod public String securedMethod() throws AppSecurityException { return "Ok"; } }And catch it in client code like this:
new AsyncCallback<String>() { @Override public void onFailure(Throwable caught) { if (caught instanceof AppSecurityException) { //Security error, handle it accordingly } else { //Some other error } } @Override public void onSuccess(String result) { // Handle result } }You can download working example here.