Securing your Flex app: smart card authentication with Spring Security (Part 2)

In this article I am going to show you how to implement Smart Card authentication mechanism in a Flex application with Spring Security. I will use X.509 certificate authentication and database authorization simultaneously. To get a full picture of the topic, you should take a look at my previous article Securing your Flex application with Spring Security and Active Directory.
The application used in this article is based on the previous sample project available for download here (current version included). You could install it and as you read, make appropriate modifications described below. Last time we integrated our Flex application with Microsoft Active Directory LDAP server by using the authentication and authorization mechanism offered by the Spring Security. Spring Security framework is an excellent tool for implementing security requirement for enterprise applications as it offers comprehensive security services for J2EE-based enterprise applications. These services are simple enough to implement even for those, who have little background in Java.
Now, I will show you how easily the login mechanism can be changed and how to mix different scenarios together. Once a user is authenticated with the smart card, the next step is to load roles from a sql server database. If the client does not have valid certificate, database authentication mechanism can be used.
|
|
Estonian ID-card issued by the state and serve as primary personal identification documents. |
What you need to test X.509 certificate authentication?
1) Tomcat application server;
2) Some relational database (MySQL);
3) Smart card.
If you don't have a smart card, you can still try it out by downloading certificate/key combinations for client authentication, issued by "Spring Security Test CA" certificate authority, and installing it in your browser (see more details below).
Living in Estonia has some advantages like having an ID-card which is a smartcard based identification document. They are issued by the state and serve as primary personal identification documents in Estonia. They are used for authentication in Internet banking environments, in public transportation ticket systems, in signing formal documents, and even in parliamentary elections. Since 1st of January 2002 over 1,000,000 cards have been issued, which marks the biggest ID card roll-out in Europe.
Step 1: Database configuration
Create a table for users, and insert some data into the created table. The standard JDBC implementation of the UserDetailsService requires tables to load the password, account status and a list of roles for the user. For simplify, insert same names as used by LDAP server.
CREATE TABLE IF NOT EXISTS USERS ( USERNAME varchar(50) NOT NULL, PASSWORD varchar(50) NOT NULL, enabled BOOLEAN NOT NULL ); INSERT INTO USERS (USERNAME, PASSWORD, ENABLED) VALUES ('user', 'user', TRUE), ('admin', 'admin', TRUE), ('scott', 'scott', TRUE);
Now create second table for roles.
CREATE TABLE IF NOT EXISTS AUTHORITIES ( USERNAME varchar(50) NOT NULL, AUTHORITY varchar(50) NOT NULL, constraint fk_authorities_users FOREIGN KEY(username) REFERENCES users(username) ); CREATE UNIQUE INDEX ix_auth_username ON authorities (username,authority); INSERT INTO AUTHORITIES (USERNAME, AUTHORITY) VALUES ('user', 'ROLE_USER'), ('scott', 'ROLE_USER'), ('admin', 'ROLE_ADMIN');
Open applicationContext-security.xml file and replace the LDAP server definition with the one below.
<s:authentication-provider user-service-ref="userDetailsService"> <s:password-encoder hash="plaintext" /> </s:authentication-provider> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/flex_db"/> <property name="username" value="root"/> <property name="password" value=""/> </bean> <bean id="userDetailsService" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean>
By adding the users-by-username-query and authorities-by-username-query properties you are able to override the default SQL statements with your own. Before restarting Tomcat don't forget to put your database JDBC driver into the webapp /lib directory. Don't go further before making sure you are authenticated and authorized by database server. Please check your log4j configuration, so you can see what's going on behind the scenes:
log4j.logger.org.springframework.security=DEBUG, stdout, fileout
Step 2: Setting up SSL in Tomcat
Typically in the production environment Servlet/JSP container runs behind another web server (Apache, IIS), not as a stand-alone web server. SSL is very computational intensive and Apache httpd is more efficient than Java-based Tomcat. But in some case, it is necessary to do. Please keep instructions on hand: Apache Tomcat 6.0: SSL Configuration HOW-TO. First thing you have to do, is add the following connector to the server.xml file:
<Connector
clientAuth="true" port="8443" minSpareThreads="5" maxSpareThreads="75"
enableLookups="true" disableUploadTimeout="true"
acceptCount="100" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="${catalina.home}/conf/server.jks"
keystoreType="JKS" keystorePass="password"
truststoreFile="${catalina.home}/conf/server.jks"
truststoreType="JKS" truststorePass="password"
SSLVerifyClient="require" SSLEngine="on" SSLVerifyDepth="2" sslProtocol="TLS"
/>
You can create a new keystore from scratch, containing a self-signed certificate, but you simplify your life if you just download pre-generated certificates from the samples/certificate directory in the Spring Security project.
The file server.jks contains the server certificate, private key and the issuing certificate authority certificate. There are also some client certificate files (scott, rod) for the users from the sample applications. Install these in your browser to enable SSL client authentication. As I am using my own ID-card, existing certificate signed by my own CA should be imported into my keystore.
keytool -import -alias root -keystore server.jks -trustcacerts -file id.crt
In the production environment you should import and handle certificate revocation list as well (using crlFile attribute). After installing and configuring SSL support, you must restart Tomcat. You should be able to access https://localhost:8443. While installing certificates issued by "Spring Security Test CA" certificate authority your browser may display a security warning:
|
|
|
| Firefox | IE 7 | Safari |
Step 3: Adding X.509 authentication to Spring-Flex application
Enabling X.509 client authentication, open applicationContext-security.xml file and add the <x509/> element to your http security namespace configuration:
<s:http> <s:intercept-url pattern="/**" requires-channel="https"/> <s:x509 subject-principal-regex="CN=(.*?)," /> <s:form-login /> <s:anonymous /> <s:logout /> </s:http>
subject-principal-regex defines a regular expression which will be used to extract the username from the certificate. This is the username which will be passed to the UserDetailsService to load the authorities for the user. The next step is to configure channels definition in services-config.xml file:
<channel-definition id="my-graniteamf-secure" class="mx.messaging.channels.SecureAMFChannel"> <endpoint uri="https://{server.name}:8443/{context.root}/graniteamf/amf" class="flex.messaging.endpoints.SecureAMFEndpoint"/> </channel-definition>
Please note that you should rebuild your swf before restarting Tomcat. Otherwise you will see a message indicating you are still using http accessing my-graniteamf-secure channel.
|
Now I can authenticate with a smart card or public key certificate:
|
|
Although I am authenticated with my smart card, database can't still authorize me. See log for details:
[DEBUG,X509PreAuthenticatedProcessingFilter,http-8443-2] preAuthenticatedPrincipal = PAAVEL, trying to authenticate ... org.springframework.security.userdetails.UsernameNotFoundException: User PAAVEL not found ...
So, I insert a new user 'PAAVEL' into the database and assign a role:
INSERT INTO USERS (USERNAME, PASSWORD, ENABLED) VALUES ('PAAVEL', 'whatever', TRUE); INSERT INTO AUTHORITIES (USERNAME, AUTHORITY) VALUES ('PAAVEL', 'ROLE_ADMIN'),
And Voilà!
[DEBUG,X509PreAuthenticatedProcessingFilter,http-8443-1] Authentication success: org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken@4e9526ea: Principal: org.springframework.security.userdetails.User@eb53c00: Username: PAAVEL; Password: [PROTECTED]; Granted Authorities: ROLE_ADMIN; Password: [PROTECTED]; Authenticated: true; Details: org.springframework.security.ui.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN

I created also a helper method that returns the user from Spring security context:
public String getMyUserName() { User user = (User) ((SecurityContext) SecurityContextHolder.getContext()).getAuthentication().getPrincipal(); return user.getUsername(); }
References
Mario Gleichmann: Spring
Gridshore: Posts tagged springframework
Granite SecureAMFChannel - AMF over HTTPS
The Solution to Flex Remoting Over SSL
Pathway from ACEGI to Spring Security 2.0
Spring Security Reference Documentation
Spring Security Test CA Sample Certificates
Apache Tomcat 6.0: SSL Configuration HOW-TO



March 26th, 2009 at 4:27 pm
[…] See also my follow up article: Securing your Flex app: smart card authentication with Spring Security […]
March 26th, 2009 at 5:31 pm
Sven, nice to find you.
More news: Swedbank Flex loan application first step is done.
All users are happy!
July 24th, 2009 at 11:41 pm
Thanks so much! This has been the best guide I’ve found. I got everything working in no time! The one thing I had trouble with is that with this setup, when a client connects, they are brought to the page they requested even if their username isn’t in the database.
I had to add
to the http tag.
Also, for flexibility, I preferred to write my own userDetailsService class. Just make the bean implement from Spring’s UserDetailsService interface and you’re good to go!
Thanks again!
April 8th, 2010 at 10:35 am
Hi, I try follow you direction but get Error Client.Error.MessageSend.
So I rebuilt swf, but I don’t know how to build. Then I also know how to
I used command
mxmlc springflex/src/smartcard/src/Main.mxml
But failed with error
Error: Type was not found or was not a compile-time constant: SecurityEvent.
public function onSecurityEvent(event:SecurityEvent):void (
I also build flex project , add 2 source file (login and main) and libs in springflex example to libs my project, but also same error.
Maybe flex can’t see jar lib file.
Please, help me how to build swf or this example !
So thanks !
Please help me how to
April 24th, 2010 at 11:59 pm
Good article.
I am using Flex 4, Tomcat 6.0.20, Spring 2.56. Can you suggest the correct files instead of
applicationContext-security.xml, to set up ? I don’t recognise this file. I do have a security-config.xml.