Integrating Spring Security with BlazeDS and Flex RIAs
I have found a couple of tutorials on this lying around the web, however they all take slightly different approaches, and although the information was useful, I think there is a cleaner way to do the integration. Keeping in mind of course that now that the spring guys have released the spring-flex module there will hopefully be complete integration with Spring Security soon without the need to even wire it up the way I describe here.
What I am going to describe here uses:
- Spring 2.5.5
- Spring-Security 2.0.4 (core & tiger)
- BlazeDS 3.2
- Flex3
My approach is not to protect end-points using BlazeDs’s security. The reason is that I often expose my services as web services as well as AMF endpoints and I don’t want to create my security mappings twice. Instead I use method invocation based security and protect the services directly. All that BlazeDS needs to do is get the users credentials out of the request and delegate to SpingSecurity to create a SecureContext, authenticate and put the authentication object in the SecureContext (effectively thread local). After that normal Spring Security applies and secure methods will be protected in the normal way.
A typical security scenario in one of my applications goes something like this: Either none of the application or a small part of the application is available anonymously as is the login service. Users access them without credentials, usually the User doesn’t even have menu options to navigate to UI areas with secure functions until they have logged in. After a successful login I will cache the user’s credentials on the client and re-log them in on each service request. This keeps the server completely stateless, all state is stored on the client. I configure a lightweight cache on the server since if someone logs in once they will likely make many more login requests (one for every service they are going to use). This reduces database hits. I implement a service registry on the client which stores the credentials and automatically adds them to all RemoteObjects and HttpServices.
I’m going to work this assuming that you already have a Flex/BlazeDS/Spring application going. It can be as simple as hello world, there is an excellent article on how to do this using the new spring-flex solution here . The way that i am applying security means that it is largely irrelevent whether you are using the Adobe SpringFactory or the spring-flex MessageBrokerHandlerAdapter.
Step1. Add spring-security to your web application’s lib my pom entry looks like this:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core-tiger</artifactId>
<version>2.0.4</version>
</dependency>
</dependencies>
Step2. Add the spring security filter to your 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>/data/messagebroker/*</url-pattern>
</filter-mapping>
This will secure everything and is mostly necessary if you are going to use other transports than just AMF via BlazeDS.
Step3. Configure Spring Security. I am just going to put a very basic config here. For details on configuring spring security you can look at the spring security documentation here
<security:http auto-config=“true”>
<security:intercept-url pattern=“/**” filters=“none”/>
</security:http>
<security:global-method-security>
<security:protect-pointcut
expression=“execution(* net.oneadam.app.services.*Manager.*(..))”
access=“ROLE_ADMIN”/>
</security:global-method-security>
<authentication-provider>
<user-service>
<user name=”adam” password=”adamspassword” authorities=”ROLE_USER, ROLE_ADMIN” />
<user name=”bob” password=”bobspassword” authorities=”ROLE_USER” />
</user-service>
</authentication-provider>
This configuration passes everything through at the URL level and relies on method level security. I have used pointcuts here since it doesn’t require me to cut and paste classes and methods in order to demonstrate how annotations work. The above pointcut requires the user for all methods in any class ending in the word Manager in the services package to have the role ROLE_ADMIN.
Step4. Implement the flex.messaging.security.LoginCommand Interface so that the doAuthentication creates a UsernamePasswordAuthenticationToken and authenticates using Spring Security, if it is successful it should put the authentication object into the SecureContext where it will be accessible to your application and the Spring Security annotations or security pointcuts. here is my implementation:
package net.oneadam.web.security;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.security.AbstractAuthenticationManager;
import org.springframework.security.Authentication;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.AbstractAuthenticationToken;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.web.context.support.WebApplicationContextUtils;
import flex.messaging.FlexContext;
import flex.messaging.io.MessageIOConstants;
import flex.messaging.security.LoginCommand;
public class SpringSecurityLoginCommand implements LoginCommand {
private static final Log LOG = LogFactory.getLog(SpringSecurityLoginCommand.class);
private AbstractAuthenticationManager authenticationManager;
public Principal doAuthentication(String username, Object credentials) {
String password = extractPassword(credentials);
if (password == null) {
return null;
}
if(authenticationManager == null) {
init();
}
Authentication auth = new UsernamePasswordAuthenticationToken(username, password);
try {
SecurityContextHolder.getContext().setAuthentication(authenticationManager.authenticate(auth));
LOG.info(“User [" + username + "] logged in.”);
return (AbstractAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
}
catch (RuntimeException e) {
LOG.info(“User [" + username + "] failed authentication.”);
throw e;
}
}
/**
* We are not going to perform Authorization at this level since we would like SpringSecurity
* to manage Authorization at the method level.
*/
@SuppressWarnings(“unchecked”)
public boolean doAuthorization(Principal principal, List roles) {
return true;
}
public boolean logout(Principal principal) {
HttpServletRequest request = FlexContext.getHttpRequest();
if (request != null && request.getSession(false) != null) {
try {
request.getSession().invalidate();
}
catch (IllegalStateException e) {
// Expected.
}
}
request.getSession(true); // Session re-created to avoid Flex error when
// it also attempts to invalidate the session.
LOG.info(“User [" + principal.getName() + "] logged out.”);
return true;
}
public void start(ServletConfig config) {
//My tests show this never gets called
}
public void stop() {
authenticationManager = null;
}
@SuppressWarnings(“unchecked”)
protected String extractPassword(Object credentials)
{
String password = null;
if (credentials instanceof String)
{
password = (String)credentials;
}
else if (credentials instanceof Map)
{
password = (String)((Map)credentials).get(MessageIOConstants.SECURITY_CREDENTIALS);
}
return password;
}
private void init() {
HttpServletRequest request = FlexContext.getHttpRequest();
ApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
authenticationManager = (AbstractAuthenticationManager) ctx.getBean(“_authenticationManager”);
}
}
You will notice that the doAuthentication method just returns true. That is because I am not interested in securing BlazeDS destinations. I prefer to secure the service facade methods themselves, that way if I expose them using CXF or some other Web Service transport my security does not have to be redone.
Step5. Wire up security in the services-config.xml file to use your SpringSecurityLoginCommand it will look something like this:
<security>
<login-command class=“com.renewtek.cmf.web.security.SpringSecurityLoginCommand” server=“Tomcat”>
<per-client-authentication>true</per-client-authentication>
</login-command>
<security-constraint id=“roleuser”>
<auth-method>Custom</auth-method>
<roles>
<role>ROLE_USER</role>
<role>ROLE_ADMIN</role>
</roles>
</security-constraint>
</security>
The roles are actually unnecessary since I return true from all authorization requests at this level. I have just left them in for completeness. If you wanted to reject a request at this level that is where you would put the roles and if you are using the Adobe style of defining destinations in your remoting-config.xml file then you could say what destinations are allowed for what roles. What is important to the strategy I described above is the per-client-authentication entry. it needs to be set to true since we don’t want to use server based sessions. it is false by default.
February 18th, 2010 at 6:39 am
hii Mr. Adam, I am a beginner
I’m working on projects related to flex, spring and java, I have problem in doing authentication to flex, can you help me to give your project file (rar / zip), because I am still confused how to integrate Spring with flex
tools that I use eclipse 3.4.2
please help me Mr. Adam, thank you