Monthly Archives: February 2013

Configuring Spring MVC for AJAX to enable SEO

AJAX based web applications always have challenges with Search engines. The fundamental aspect of AJAX is the content bound to AJAX components are not statically bound when the page loads, but are partially rendered as and when a page events occur and loaded in Javascript DOM tree. Search engines like Google and Bing only indexes static contents.

One of our applications has been built using modern web technologies and was completely Ajax based. We extensively used Backbone.js and other AJAX based technologies. And our application was not optimized for Search engines. As our application was built around Spring MVC so as a first step, I wanted to explore if Spring supports SEO. I found a blog from MKYong Spring 3 MVC ContentNegotiatingViewResolver example. We tried this example and it was slightly complicated to setup our application using this technique.

On further research we found an interesting article in Google Webmasters link. As per this link for every request if we pass a “_escaped_fragment_” the Search Engine are capable of indexing these links. In order enable this in a Spring MVC application; we need to use a framework like Phantom.js. Phantom.js has a capability where in it will take all the data in a DOM tree and output HTML content.

As a 1st step create a Phantom.js script as below,

var page = require('webpage').create();
var system = require('system');

// Open the page
page.open(system.args[1], function (status) {
console.log(page.content);
phantom.exit();
});

We need to execute this code from Java and pass the server returned content to this code as below,

public static String contentBuilderHelper(String redirectUrl){
pbCommand.add("phantom-server.js");
pbCommand.add(redirectUrl);
ProcessBuilder builder = new ProcessBuilder(pbCommand);

final Process process = builder.start();
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
output = output + line;
}
return output;
}

Configure Spring MVC component is we need to create a custom filter as below,

public class CrawlerFilter implements Filter{
...
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if(httpRequest.getParameter("_escaped_fragment_")!=null){
String redirectUrl = httpRequest.getRequestURL().toString()+"#!"+httpRequest.getParameter("_escaped_fragment_");
redirectUrl = URLDecoder.decode(redirectUrl,"UTF-8");
System.out.println(redirectUrl);
try {
//Get content from the web
String content = contentBuilderHelper(redirectUrl);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.write(content);
out.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chain.doFilter(request, response);
}
...
}

Configuring filter in web.xml

<filter>
<filter-name>GoSmarter Crawler Filter</filter-name>
<filter-class>com.example.filter.CrawlerFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>Crawler Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Finally, we also need to configure Phantom.js framework in Tomcat server.

I hope this blog helped you.

Advertisements

Sample of Spring Security and CAS (Single Signon)

For people in hurry get the code from Github.

In continuation of my earlier blog on spring-test-mvc junit testing Spring Security layer with InMemoryDaoImpl, in this blog I will be doing the following,

  • Run a sample Jasig, deployed in tomcat instance
  • Run a sample Spring security Calendar application with uses single signon to do authentication and authorization of the application

Setup of Central Authorization Service (CAS): Jasig

Central Authorization Service (CAS) is a Single Signon application used to authenticate and authorize an application for centralized user access control. Jasig is an opensource CAS application. In order for Jasig to work we need to enable SSL.

<Connector SSLEnabled="true"
maxThreads="150"
port="8443"
protocol="HTTP/1.1"
scheme="https"
secure="true"
sslProtocol="TLS"
keystoreFile="${catalina.base}/conf/tomcat.keystore"
keystorePass="changeit"
truststoreFile="${catalina.base}/conf/tomcat.truststore"
trustStorePass="changeit"/>

Run the Calendar application against CAS

Build the code from spring-security-cas project using maven command “mvn clean package” and deploy the war file in the target folder into <Tomcat home>/webapps folder. Start the Tomcat instance. If you go to the calendar home page @ https://localhost:8443/calendar, you will see the webpage as below,

spring-security calendar application

spring-security calendar application

When you click on My Events we get the below Jasig page,

CAS jasig

CAS jasig

Enter login/password as user1@example.com/user1. You will get the access to your events.

When you log out, it will again redirect to Jasig logout page.

What is happening under the hood on Jasig side

Under the hood, Jasig CAS server is configured to work with inmemory LDAP as below,

<sec:ldap-server id="contextSource" ldif="classpath:ldif/calendar.ldif" root="dc=jbcpcalendar,dc=com" />

And mapping of LDAP attributes to CAS attributes happens in the below bean definition,

<!--
Bean that defines the attributes that a service may return.  This example uses the Stub/Mock version.  A real implementation
may go against a database or LDAP server.  The id should remain "attributeRepository" though.
-->
<bean id="attributeRepository">
<property name="contextSource" ref="contextSource"/>
<property name="requireAllQueryAttributes" value="true"/>
<property name="baseDN" value="ou=Users"/>
<property name="queryAttributeMapping">
<map>
<entry key="username" value="uid"/>
</map>
</property>
<property name="resultAttributeMapping">
<map>
<entry key="cn" value="FullName"/>
<entry key="sn" value="LastName"/>
<entry key="description" value="role"/>
</map>
</property>
</bean>

Check the deployerConfigContext.xml under webapp/WEB-INF folder in the project for more details

What is happening under the hood on Calendar application

In the web.xml we need to include a SingleSignon listener as below,

<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>

We need to define a casAuthProvider bean and include it in the authentication-manager in the security config as below,

<authentication-manager alias="authenticationManager">
<authentication-provider ref="casAuthProvider" />
</authentication-manager>

Refer src\main\webapp\WEB-INF\spring\security-cas.xml file for more details on casAuthProvider bean definition.

Maven dependency for Spring Security with CAS is as below,

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>3.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>

I hope this blog helped you.

REFERENCE

Spring Security 3.1 by Robert Winch and Peter Mularien

spring-test-mvc junit testing Spring Security layer with Method level Security

For people in hurry get the code from Github.

In continuation of my earlier blog on spring-test-mvc junit testing Spring Security layer with InMemoryDaoImpl, in this blog I will discuss how to use achieve method level access control. Please follow the steps in this blog to setup spring-test-mvc and run the below test case.

mvn test -Dtest=com.example.springsecurity.web.controllers.SecurityControllerTest

The Junit test case looks as below,

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = WebContextLoader.class, value = { "classpath:/META-INF/spring/services.xml",
"classpath:/META-INF/spring/security.xml",
"classpath:/META-INF/spring/mvc-config.xml" })
public class SecurityControllerTest {

@Autowired
CalendarService calendarService;

@Test
public void testMyEvents() throws Exception {
Authentication auth = new UsernamePasswordAuthenticationToken("user1@example.com", "user1");
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);

calendarService.findForUser(0);
SecurityContextHolder.clearContext();
}

@Test(expected = AuthenticationCredentialsNotFoundException.class)
public void testForbiddenEvents() throws Exception {
calendarService.findForUser(0);
}
}
@Test(expected=AccessDeniedException.class)
public void testWrongUserEvents() throws Exception {
Authentication auth = new UsernamePasswordAuthenticationToken("user2@example.com", "user2");
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);

calendarService.findForUser(0);
SecurityContextHolder.clearContext();
}

If you notice, if the user did not login or if the user is trying to access another users information it will throw an exception.

The interface access control is as below,

public interface CalendarService {

@PreAuthorize("hasRole('ROLE_ADMIN') or principal.id == #userId")
List<Event> findForUser(int userId);
}

The PreAuthorize only works on interface so that any implementation that implements this interface has this access control.

I hope this blog helps you. In my next blog I will explain how you integrate Spring Security with Central Authorization Service (CAS) Single Sign On.

REFERENCE

Spring Security 3.1 by Robert Winch and Peter Mularien

spring-test-mvc junit testing Spring Security layer with LDAP

For people in hurry get the code from Github.

In continuation of my earlier blog on spring-test-mvc junit testing Spring Security layer with InMemoryDaoImpl, in this blog I will discuss how to use Spring Security’s LDAP integration.

Please follow the steps in this blog to setup spring-test-mvc and run the below test case,

mvn clean test -Dtest=com.example.springsecurity.web.controllers.Video4LdapProviderControlerTest

The dependency required integrating LDAP is as below,

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>3.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-protocol-ldap</artifactId>
<version>1.5.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.shared</groupId>
<artifactId>shared-ldap</artifactId>
<version>0.9.15</version>
<scope>compile</scope>
</dependency>

In this example we used inmemory LDAP configured to work with calendar.ldif for all user/role information. In the real life, there will be a enterprise quality LDAP like Active Director configured with Spring Security. For configuring this in spring security configuration you need to add below code,

<ldap-server id="ldapServer" ldif="classpath:ldif/calendar.ldif" root="dc=jbcpcalendar,dc=com" />

The plumbing for spring-test-mvc to work with LDAP is in the class com.example.springsecurity.web.controllers.util.LdapSecurityRequestPostProcessors. The below code does the magic,

private UsernamePasswordAuthenticationToken authentication(ServletContext servletContext) {
ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);

FilterBasedLdapUserSearch filterBasedLdapUserSearch = context.getBean(FilterBasedLdapUserSearch.class);

DirContextOperations ldapUserDetails = filterBasedLdapUserSearch.searchForUser(username);

List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(1);
authorities.add(new SimpleGrantedAuthority(ldapUserDetails.getStringAttribute("sn")));

return new UsernamePasswordAuthenticationToken(username, ldapUserDetails.getObjectAttribute("userpassword").toString(),
authorities);
}

spring bean definition for FilterBasedLdapUserSearch is as below,

<bean id="ldapSearch">
<constructor-arg value="ou=users"/> <!-- use-search-base -->
<constructor-arg value="(uid={0})"/> <!-- user-search-filter -->
<constructor-arg ref="ldapServer"/>
</bean>

I hope this blog helped. In my next blog I will be explaining how to integrate Spring Security with Method level access control.

REFERENCE

Spring Security 3.1 by Robert Winch and Peter Mularien

spring-test-mvc junit testing Spring Security layer with JdbcDaoImpl

In continuation of my earlier blog on spring-test-mvc junit testing Spring Security layer with InMemoryDaoImpl, in this blog I will discuss how to use Spring Security’s JdbcDaoImpl class.

In this blog, we will be discussing same usecase and same code base as in this blog, the only difference being for testing this feature you need to run different testcase as below,

mvn clean test -Dtest=com.example.springsecurity.web.controllers.Video3JdbcUserServiceControllerTest

The database schema for user authorization is as below,

create table calendar_users (
id bigint identity,
email varchar(256) not null unique,
password varchar(256) not null,
first_name varchar(256) not null,
last_name varchar(256) not null
);

create table calendar_user_authorities (
id bigint identity,
calendar_user bigint not null,
authority varchar(256) not null,
);

Below is the JDBC configuration to identify the user and his role/authorization to access.

<authentication-manager>
<authentication-provider>
<jdbc-user-service id="userDetailsService"
data-source-ref="dataSource"
users-by-username-query="select email,password,true from calendar_users where email = ?"
authorities-by-username-query="select cua.id, cua.authority from calendar_users cu, calendar_user_authorities cua where cu.email = ? and  cu.id = cua.calendar_user"/>
</authentication-provider>
</authentication-manager>

I hope this blog helped you. In my next blog, I will demo how to use LDAP for Securing an application.

REFERENCE

Spring Security 3.1 by Robert Winch and Peter Mularien

spring-test-mvc junit testing Spring Security layer with InMemoryDaoImpl

For people in hurry here is the code sample and the steps to setup the code.

Spring Security 3.1 is a framework that supports authentication and authorization in Java. Spring MVC has a good integration with Spring Security. In this blog, I will demonstrate how to JUnit test Spring Security layer for a Spring MVC application using spring-test-mvc.

The usecase we use in this blog is a Spring MVC based calendar application, where user can login and enter his events his events. User can see other user events but he cannot change it. An admin user can see other user’s Events as well as change it.

To enable spring-test-mvc, we need to setup spring-test-mvc. As a first setp get the latest code from github. and run the “mvn install” command in the downloaded folder. After that we need to add the below dependency in the pom.xml,

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test-mvc</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<scope>test</scope>
</dependency>

For a starter I will start writing the JUnit test as below to capture above usecase,

@Before
public void setup() {
mockMvc = MockMvcBuilders.webApplicationContextSetup(this.wac).addFilters(this.springSecurityFilterChain).build();
}

@Test
public void testIndex() throws Exception {
mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(forwardedUrl("/WEB-INF/views/index.jsp"));
}

@Test
public void testEventsNonAdmin() throws Exception {
mockMvc.perform(get("/events/").with(userDeatilsService("user1@example.com"))).andExpect(status().isForbidden());
}

@Test
public void testEventsAdminLogin() throws Exception {
mockMvc.perform(get("/events/").with(userDeatilsService("admin1@example.com"))).andExpect(status().isOk());
}

@Test
public void testMyEvents() throws Exception {
mockMvc.perform(get("/events/my").with(userDeatilsService("user1@example.com"))).andExpect(status().isOk());
}

@Test
public void testShow() throws Exception {
mockMvc.perform(get("/events/100").with(userDeatilsService("user1@example.com"))).andExpect(status().isOk());
}

@Test
public void testCreateEventForm() throws Exception {
mockMvc.perform(
post("/events/new").param("attendeeEmail", "user1@example.com").param("summary", "krishna").param("description", "whatever")
.param("when", "2012-01-01 01:02").with(userDeatilsService("user1@example.com"))).andExpect(status().isOk())
.andExpect(redirectedUrl("/events/my"));
}

To wire Spring Security with JUnit test you need to do as below,

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = WebContextLoader.class, value = {
"classpath:/META-INF/spring/video1-spring-inmemory-userdetailservice-config/services.xml",
"classpath:/META-INF/spring/video1-spring-inmemory-userdetailservice-config/security.xml",
"classpath:/META-INF/spring/video1-spring-inmemory-userdetailservice-config/mvc-config.xml" })
public class Video1SpringInmemoryUserdetailServiceControllerTest {

The Spring security config file looks as below,

<http pattern="/resources/**" security="none"/>
<http auto-config="true" use-expressions="true" create-session="ifRequired">
<intercept-url pattern="/" access="hasRole('ROLE_ANONYMOUS') or hasRole('ROLE_USER')"/>
<intercept-url pattern="/login/*" access="hasRole('ROLE_ANONYMOUS') or hasRole('ROLE_USER')"/>
<intercept-url pattern="/logout" access="hasRole('ROLE_ANONYMOUS') or hasRole('ROLE_USER')"/>
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/events/" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>

<form-login login-page="/login/form"
login-processing-url="/login" username-parameter="username" password-parameter="password"
authentication-failure-url="/login/form?error"/>
<logout logout-url="/logout" logout-success-url="/login/form?logout"/>
</http>

<authentication-manager>
<authentication-provider>
<user-service>
<user name="user1@example.com" password="user1" authorities="ROLE_USER"/>
<user name="admin1@example.com" password="admin1" authorities="ROLE_USER,ROLE_ADMIN"/>
</user-service>
</authentication-provider>
</authentication-manager>

In this configuration there are 2 blocks one is http block that describes authentication and authentication-manager that describes authorization. In this example, for authorization we are using org.springframework.security.core.userdetails.memory.InMemoryDaoImpl helper class. To make this example work run the below command,


mvn clean test -Dtest=com.example.springsecurity.web.controllers.Video1SpringInmemoryUserdetailServiceControllerTest

In my next blog I will be introducing Jdbc implementation of authorization.

REFERENCE

Spring Security 3.1 by Robert Winch and Peter Mularien