Introduction

This post explains how to get the user service from RMI server. In this example I have used simple UserDetailsService which will check weather the username and password are same, but you can use any implementation the change will in server side only it wont affect the client.

Target audience: I assume that you have basic knowledge about JEE, Spring Framework, Spring Web and Spring Security. If not this post may be not for you, please go through those topics, there are lots of good resources in the Internet, I mostly refer Mkyong’s Spring Tutorial

Development Environment

For this example I used JDK 7 and Maven 3.0.4. I hope it will work on JDK 6 also.

Project Structure

In the example we will use two Web applications. One as server and another as the client. The flow will be user will login on the client, the authentication provider will use UserDetails service to fetch the User object for the given username which will be a remote call. In this example RMI is used but it can be implemented on HTTP Invoker also.

Steps

The following are the steps to create RMI server and client application.

The Server

1 . Create a JEE web application. I use maven to generate a web app archetype, but you can create in any way. The reason I use maven it is easy to maintain the dependencies and build.

2 . The following libraries should be added in class path. (If you are using maven add these as dependency in your pom. please refer the Github project)

aopalliance-1.0.jar
jcl-over-slf4j-1.7.2.jar
logback-classic-1.0.9.jar
logback-core-1.0.9.jar
slf4j-api-1.7.2.jar
spring-aop-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
spring-jdbc-3.0.7.RELEASE.jar
spring-security-config-3.1.3.RELEASE.jar
spring-security-core-3.1.3.RELEASE.jar
spring-security-remoting-3.1.3.RELEASE.jar
spring-security-web-3.1.3.RELEASE.jar
spring-tx-3.0.7.RELEASE.jar
spring-web-3.2.0.RELEASE.jar

3 . If the web application created is not servlet 2.5 change the WEB-INF/web.xml namespace to make it as Servlet 2.5

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name>Spring Remoting Server</display-name>
</web-app>

4 . Add org.springframework.web.context.ContextLoaderListener listener and contextConfigLocation context param. web.xml will look like follows.

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>Spring Remoting Server</display-name>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
</web-app>

5 . Now we have to update the server spring configuration. In web.xml we mentioned that context configuration xml, so create a xml file with the same name with the following content.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <bean id="userServiceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">
        <property name="serviceName" value="UserService"/>
        <property name="service" ref="userDetails"/>
        <property name="serviceInterface" value="org.springframework.security.core.userdetails.UserDetailsService"/>
        <!-- defaults to 1099 -->
        <property name="registryPort" value="1099"/>
    </bean>
    <bean id="userDetails" class="com.seenukarthi.springremoting.server.security.userdetails.UserDetailsManagerImpl"/>
</beans>

In the above configuration there are two beans. Bean with id userServiceExporter is the RMI service exporter.

Properties explained.

serviceName
This is the name of the service exposed.
service
This is the bean reference of the service implementation.
serviceInterface
This is the fully qualified name of the interface which the service implementation is implemented.
registryPort
This is RMI registry port which the client will access the server.

Bean with id userDetails is the service implementation of org.springframework.security.core.userdetails.UserDetailsService This is simple implementation it returns UserDetails objects with ROLE_USER authority.

package com.seenukarthi.springremoting.server.security.userdetails;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class UserDetailsManagerImpl implements UserDetailsService {

	@Override
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {
		return new User(username, username, AuthorityUtils.createAuthorityList("ROLE_USER"));
	}

}

That’s all in server side.

The Client

First three steps are same as server. So we will start with step 4.

4 . Add org.springframework.web.context.ContextLoaderListener listener, contextConfigLocation context param and org.springframework.web.filter.DelegatingFilterProxy filter. The different between server and client is the security filter. web.xml will look like follows.

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<display-name>Spring Remoting Web client</display-name>

	<context-param>
    	<param-name>contextConfigLocation</param-name>
    	<param-value>/WEB-INF/applicationContext.xml</param-value>
	</context-param>

	<!-- For session events -->
	<listener>
		<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
	</listener>
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<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>/*</url-pattern>
	</filter-mapping>

</web-app>

5 . Similar to server create an application context and security context config files with the following content.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

	<import resource="security.xml"/>

	<bean id="userService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://localhost:1099/UserService" />
		<property name="serviceInterface" value="org.springframework.security.core.userdetails.UserDetailsService" />
	</bean>

</beans>


<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <http auto-config="true">
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
        <form-login/>
    </http>

    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="userService"/>
    </authentication-manager>

</beans:beans>

Here we have two configuration files the reason for that is spring security uses different namespace from spring beans so it will be easy to maintain, but it can be in single file.

In the above configuration there are a bean, a http configuration and an authentication manager the bean will be used as userService. Bean with id userService is the RMI service consumer.

Properties explained.

serviceUrl
This is the RMI URL where the service is exposed.
serviceInterface
This is the fully qualified name of the interface which the service implementation is implemented. This should be same as serviceInterface used in server

The example

Source code

The example code is in Github here. If you have a git client you can clone as follows.

$ git clone git://github.com/seenukarthi/spring-remoting-sec-example.git

If you don’t have a git client you can download the source using the following links and extract it. Tarball or Zip file

Building and Testing

Change the directory to project root directory in the Terminal/Cmd Prompt run the following command to build.

$ mvn clean install

To run this project open two terminal on project root folder.

Terminal 1

$ cd spring-remoting-server
$ mvn clean package tomcat:run -Dmaven.tomcat.port=8081

Terminal 2

$ cd spring-remoting-webclient
$ mvn clean package tomcat:run

Open http://localhost:8080/spring-remoting-webclient in the browser and and give admin/admin as credentials.

Please feel free to comment.