Integrating Blaze Data Services and Spring Security

One thing to keep in mind with the out-of-the-box security support in Blaze DS is the approach to integration is container-specific: there is support for Tomcat (and therefore JBoss), WebSphere, Weblogic and Oracle through various implementations of the LoginCommand interface. Unfortunately if you have custom security requirements for authentication that means you're dealing with a lot of cumbersome, container-specific security configuration and/or writing and configuring JAAS plugins. The authorization support in Blaze DS is limited to specifying which roles have access to a particular destination which isn't nearly flexible enough.

Fortunately Spring Security provides solutions to many common problems in Java EE space, including features like container portability, a flexible authentication provider model, authorization of service method invocation via AOP and even some very cool ACL support to enforce granular security at the domain object level. Integrating Spring Security with Blaze DS isn't as hard as you think either: I was able to bang out a quick proof of concept over a weekend.

The config for Spring Security 2 is quite straightforward with the new XML namespace support in your Spring config files:

<security:http>
<security:form-login>
</security:form-login>

This is basically a very stripped-down configuration since things like RememberMeServices (using cookies) don't usually apply in a Flex-based RIA. You can also throw in a very simple AuthenticationProvider like this one from the SS2 docs:

<security:authentication-provider>
<security:user-service>
<security:user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="bob" password="bobspassword" authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>

There are two items you need to add to your web.xml to bootstrap SS2 in a Servlet container:

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter>
<filter-name>securityContextAwareFilter</filter-name>
<filter-class>org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</filter-mapping>
</filter-mapping>

<filter-mapping>
<filter-name>securityContextAwareFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

The first filter is standard part of any Spring Security configuration. The "SecurityContextHolderAwareRequestFilter" adapts SS2 to the Servlet environment so that calls like getPrincipal() and isUserInRole() behave as expected. This really comes in handy when you have other code in your projects that assumes a "standard" Java security setup.

Now we need a little config in the Blaze services-config.xml file:

<security>
<login-command class="net.histos.util.spring.SpringSecurityLoginCommand" server="Tomcat"/>
<security-constraint id="valid-user">
<auth-method>Custom</auth-method>
<roles>
<role>ROLE_USER</role>
</roles>
</security-constraint>
</security>

Blaze DS seems to require that a "server" attribute be specified for any LoginCommand even though this isn't really used in our implementation. The security-constraint isn't necessary if you are going to use SS2's service method invocation authorization support. However if your security requirements are more straightforward you can do role/destination based restrictions here. Then simply add this element to the appropriate destinations:

<security>
<security-constraint ref="valid-user"/>
</security>

The last part is some Java code. If you extend AppServerCommand you get a default impl for this method:

protected boolean doAuthorization(Principal principal, List roles, HttpServletRequest request) throws SecurityException

This method makes use of isUserInRole(), so by adding the servlet filter referred to above, this logic can work without any modification required. This leaves only two methods in your LoginCommand impl:

public Principal doAuthentication(String username, Object credentials) {
log.debug("doAuthentication");
// get the ProviderManager from app context
Map<string, providermanager=""> map = getContext().getBeansOfType(ProviderManager.class);
if (map.size() != 1)
throw new RuntimeException("Spring ApplicationContext must contain exactly one ProviderManager bean");
ProviderManager provider = map.get( map.keySet().iterator().next() );
// authenticate
String password = extractPassword(credentials);
Authentication auth = provider.authenticate( new UsernamePasswordAuthenticationToken(username, password) );
SecurityContextHolder.getContext().setAuthentication(auth);
return auth;
}

public boolean logout(Principal principal) {
log.debug("logout");
SecurityContextHolder.getContext().setAuthentication(null);
return true;
}

Those are the basics! As I said, this a proof of concept that I haven't had time to test extensively yet but it should get you started! One other note: I noticed while testing the Flex side that calling login() or logout() on a RemoteObject without calling a "regular" service method would result an error; apparently the ChannelSet hadn't been defined yet. I did a little digging and found that apparently you're supposed to call login() or logout() on the underlying ChannelSet itself. I wrote a very simple ChannelSet implementation that can be easily instantiated in MXML and bound as the channelSet property for your RemoteObjects:

package net.histos.flex.util
{
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;

public class SimpleChannelSet extends ChannelSet
{
public function set url(channelUrl : String) : void {
addChannel(new AMFChannel("defaultChannel", channelUrl));
}
}
}

<util:SimpleChannelSet id="channelSet" url="http://localhost:8080/testapp/messagebroker/amf"/>

Then simply invoke login() and logout() on the ChannelSet itself and you should have no problems.

Maven, Eclipse and WTP Integration

The Q4E Maven plugin for Eclipse now plays nicely with WTP projects!  This was incorporated into the 0.6 release.  The other big player in this space is m2eclipse which has also recently added support for WTP.  The last time I played with Maven was a few months back but this gives me a few reasons to try and use it in my next project.  And if you're interested in the differences between the two, check out this quick comparison.

Spring Security 2 Released

The new 2.0 version of Spring Security (formerly Acegi Security) was recently released.  Some good information is available in a couple spots:

http://blog.springsource.com/main/2008/04/17/spring-security-20-final-release-no-more-dead-fairies/

http://blog.springsource.com/main/2007/12/06/whats-new-in-spring-security-2/

The new version promises to use a lot less XML configuration which was a huge detractor for the 1.x version.  Additionally the Domain Object Security looks interesting; think "ACLs for objects."  Docs on this module are available here:

http://static.springframework.org/spring-security/site/reference/html/domain-acls.html

Spring AOP: CGLIB or JDK Dynamic Proxies?

Even if you're not a big fan of Aspect-Oriented Programming, if you use the Spring framework's transaction management your application will be using dynamic AOP proxies, albeit behind-the-scenes. Spring can use two
different techniques for creating proxies at runtime: CGLIB or JDK dynamic proxies.

If the target class implements one or more interfaces, then Spring will create a JDK dynamic proxy that implements every interface. If the target class implements no interfaces, Spring will use CGLIB to create a new class on the fly that is a subclass ("extends") the target class. This leads to one important difference: a JDK dynamic proxy cannot be casted to the original target class because it's simply a dynamic proxy that happens to implement the same interface(s) as the target. This has the effect of "nudging" you to program to interfaces if they're being used in your application's model, since proxies will usually be invoked through those interfaces. 

On the other hand, if interfaces are completely absent from your model, Spring will create CGLIB proxies that can be treated more-or-less just like the target class itself. There is also a way to force creation of CGLIB proxies in either scenario that is detailed in the Spring docs right here.

If you're receiving strange ClassCastExceptions when working with your Spring-managed service layer, hopefully this tip will help you!

Easier testing with the Spring framework

Spring comes with some extremely useful convenience classes for test automation using JUnit.  In order to use them, be sure to add the spring-mock.jar to your project.  Here's a rundown of what's available in the org.springframework.test package:

  • AbstractSpringContextTests: abstract subclass of JUnit's TestCase.  This class isn't very useful on its own, but it maintains a Spring context using a static Map to optimize performance while running tests.  Due to a "design decision" in JUnit 3, classes are destroyed/recreated between each test method invocation, so persisting state across method calls can't be done by placing code in the test case's constructor.  Storing dependencies in a static variable - while considering something of a "hack" - is a welcome improvement.
  • AbstractDependencyInjectionSpringContextTests: abstract subclass of AbstractSpringContextTests.  This class will read Spring XML files from the classpath and autowire any dependencies into your test case.  Simply override getConfigLocations() and ensure that a setter exists for each dependency and Spring will do autowiring by type.
  • AbstractTransactionalSpringContextTests: abstract subclass of AbstractDependencyInjectionSpringContextTests.  This class will wrap test method calls in a transaction and automatically rollback after execution, vastly increasing performance and eliminating the need for messy setUp() and tearDown() code to setup the database.  You must provide a bean in the context that implements PlatformTransactionManager in order for the transactionality to work.
  • AbstractTransactionalDataSourceSpringContextTests: abstract subclass of AbstractTransactionalSpringContextTests.  This class will autowire a DataSource bean into your test case, making database connectivity relatively painless.

These classes have been a lifesaver and the performance increases help make running tests fast and easy.  One important note: these test classes still use the JUnit 3.x API.  If you've moved to JUnit 4, you may need to write an adapter class or wait for the Spring team to release some updated code.