Spring RMI with Remote UserDetailsManager
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.
- Anaconda Proxy Repository in Nexus OSS 3
- Node Command Line Interface Tool in TypeScript.
- Continuous Deployment for Jekyll using Bitbucket Pipeline to deploy in Github
- Grunt Watch and Livereload (Using BrowserSync) in Jekyll
- Java Thick Client with Kerberos for RESTful Service
- Install Gradle in Cloud9 IDE
- Localhost Authentication for Spring Kerberos
- JasperReport with Gradle
- Jasper Reports Font Extension
- JDK Folder from Installation EXE