Masoud Kalali has a software engineering degree and has been working on software development projects since 1998. He has experience with a variety of technologies (.NET, J2EE, CORBA, and COM+) on diverse platforms (Solaris, Linux, and Windows). His experience is in software architecture, design, and server-side development.

Masoud has published several articles at Java.net and Dzone. He has authored multiple refcards, published by Dzone, including but not limited to Using XML in Java, Java EE Security and GlassFish v3 refcardz. He is one of the founder members of NetBeans Dream Team and a GlassFish community spotlighted developer. Recently Masoud's new book, GlassFish Security has been published which covers GlassFish v3 security and Java EE 6 security.

Masoud's main area of research and interest includes service-oriented architecture and large scale systems' development and deployment and in his leisure time he enjoys photography, mountaineering and camping. Masoud's can be followed at his Twitter account.

Masoud has posted 82 posts at DZone. You can read more from them at their website. View Full User Profile

Using Spring Security to Enforce Authentication and Authorization on Spring Remoting Services Invoked From a Java SE Client

03.31.2010
| 19669 views |
  • submit to reddit

Spring framework is one of the biggest and the most comprehensive frameworks Java Community can utilize to cover most of the  end to end requirement of a software system when it come to implementation.
Spring Security and Spring Remoting are two important parts of the framework which covers security in a descriptive way and let us have remote invocation of a spring bean methods using a local proxy.

In this entry I will show you how we can use spring security to secure a spring bean exposed over HTTP and invoke its secured methods from an standalone client. In our sample we are developing an Spring service which returns the list of roles which are assigned to that currently authenticated user. I will develop a simple web application congaing an secured Spring service then I will develop a simple Java SE client to invoke that secured service.

To develop the service we need to have a service interface which is as follow:

package springhttp;
public interface SecurityServiceIF {
public String[] getRoles();
}



Then we need to have an implementation of this interface which will do the actual job of extracting the roles for the currently authenticated user.

package springhttp;
public class SecurityService implements SecurityServiceIF {

public String[] getRoles() {
Collection<GrantedAuthority> col = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
String[] roles = new String[col.size()];
int i = 0;
for (Iterator<GrantedAuthority> it = col.iterator(); it.hasNext();) {

GrantedAuthority grantedAuthority = it.next();
roles[i] = grantedAuthority.getAuthority();
i++;
}
return roles;
}
}



Now we should define this remote service in a descriptor file. Here we will use remote-servlet.xml file to describe the service. The file can be placed inside the WEB-INF of the web application. The file content is as follow:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<bean name="/securityService"
class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="serviceInterface" value="springhttp.SecurityServiceIF" />
<property name="service" ref="securityService" />
</bean>

</beans>



We are simply using /securityService as the relative URL to expose our SecurityService implementation.

The next configuration file is the Spring application context in which we define all of our beans, beans weaving and configurations. The file name is applicationContext.xml and it is located inside the WEB-INF directory.

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

<bean id="securityService" class="springhttp.SecurityService">
</bean>

<security:http realm="SecRemoting">
<security:http-basic/>
<security:intercept-url pattern="/securityService" access="ROLE_ADMIN" />
</security:http>

<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service id="uds">
<security:user name="Jami" password="Jami"
authorities="ROLE_USER, ROLE_MANAGER" />
<security:user name="bob" password="bob"
authorities="ROLE_USER,ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>


<bean id="digestProcessingFilter"
class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
<property name="userDetailsService" ref="uds" />
<property name="authenticationEntryPoint"
ref="digestProcessingFilterEntryPoint" />
</bean>

<bean id="digestProcessingFilterEntryPoint"
class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
<property name="realmName" value="ThisIsTheDigestRealm" />
<property name="key" value="acegi" />
<property name="nonceValiditySeconds" value="10" />
</bean>

<bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<security:filter-chain pattern="/**"
filters="httpSessionContextIntegrationFilter,digestProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor" />
</security:filter-chain-map>
</bean>

<bean id="httpSessionContextIntegrationFilter"
class="org.springframework.security.web.context.HttpSessionContextIntegrationFilter" />
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<property name="decisionVoters">
<list>

<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</property>
</bean>

<bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint"
ref="digestProcessingFilterEntryPoint" />
</bean>
</beans>



Most of the above code is default spring configurations except for the following parts which I am going to explain in more details. The first snippet is defining the bean itself:

<bean id="securityService" class="springhttp.SecurityService">
</bean>



The second part is when we specify the security restrictions on the application itself. We are instructing the spring security to only allow an specific role to invoke the securityService

<security:http realm="SecRemoting">
<security:http-basic/>
<security:intercept-url pattern="/securityService" access="ROLE_ADMIN" />
</security:http>



The third part is when we define the identity repository where our users and role assignment are stored.

<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service id="uds">
<security:user name="jimi" password="jimi"
authorities="ROLE_USER, ROLE_MANAGER" />
<security:user name="bob" password="bob"
authorities="ROLE_USER,ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>



As you can see we are using the simple in memory user service, you may configure the an LDAP, JDBC, or a custom user service in the production environment. All other parts of the applicationContext.xml are Spring security filter definition. You can find explanation about any of then in Spring Security Documentation or by googling for it. To enforce security restrictions on the SecurityService for any local invocation we can simply change the first disucssed snippet as follow to allow ROLE_ADMIN and ROLE_MANAGER to invoke the service locall.

<bean id="securityService" class="springhttp.SecurityService">
<security:intercept-methods>
<security:protect
method="springhttp.SecurityService.getRoles" access="ROLE_MANAGER" />
</security:intercept-methods>
</bean>


Now that we have all Spring configuration files in place, we need to add some elements to web.xml in order to let spring framework kick start. Following changes need to be included in the web.xml file.

 

<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>
<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>
<servlet>
<servlet-name>remote</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remote</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>



Now that the server part is over, we can develop the client application to invoke this service we developed. The code for the client class is as follow:

package client;
public class Main {

public static void main(final String[] arguments) {
final ApplicationContext context =
new ClassPathXmlApplicationContext(
"client/spring-http-client-config.xml");
String user = "bob";
String pw = "bob";
SecurityContextImpl sc = new SecurityContextImpl();
Authentication auth = new UsernamePasswordAuthenticationToken(user,
pw);

sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);

String[] roles = ((SecurityServiceIF) context.getBean("securityService")).getRoles();
for (int i = 0; i < roles.length; i++) {
System.out.println("Role:" + roles[i]);
}

}
}




As you can see we are initializing the application context using a xml file. We will discuss that XML file in few minutes. After the application context initialization we are creating a SecurityContextImpl,   and a UsernamePasswordAuthenticationToken  then we pass the token to the security context and finally use this security context to pass it on to the server for authentication and further authorization.

Finally we invoke the getRoles() method of our SecurityService which returns the set of roles assigned to the currently authenticated user and print its role to the default output stream.

Now, the spring-http-client-config.xml 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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<bean id="securityService"
class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8084/sremoting/securityService" />
<property name="serviceInterface" value="client.SecurityServiceIF" />
<property name="httpInvokerRequestExecutor">
<bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />
</property>
</bean>
</beans>




As you can see we are just setting some properties like serviceURL bean name, the httpInvokerRequestExecutor and so on. the only thing which you may need to change to run the sample is the serviceUrl.
You can download the complete sample code form here. These are two NetBeans project, a web application and a Java SE one. You may need to add Spring libraries to the project prior to any other attempt.

The SecurityService implementation is borrowed from Elvis weblog available at http://elvisfromhell.blogspot.com/2008/06/this-was-hard-one.html

From http://weblogs.java.net/blog/kalali/archive/2010/03/18/using-spring-security-enforce-authentication-and-authorization-spring

Published at DZone with permission of its author, Masoud Kalali.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Rehman Khan replied on Sat, 2012/02/25 - 4:41am

which version of spring and spring security have you used for this example?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.