Social Icons

Pages

Featured Posts

Monday, September 29, 2014

MySQL Import and Export

To import and export and entire database schema and data, follow these steps:
mysqldump -h host -u user -p --databases database-name > dump.sql # Export from a database
sed '/^\/\*\!50013 DEFINER/d' dump.sql > dump_0.sql # Fix the permission issues in case you have views
mysql -u user -p database-name < dump_0.sql         # Import to database

Tuesday, September 23, 2014

Spring Security and Ajax Session Expiry

One of the requirement in a recent project was to capture the Ajax session expiry and send a JSON response back to the client if the session was expired. There are a few different ways to handle this in Spring Security. Here is one approach

<bean id="ajaxRequestMatcher" class="com.mycompany.AjaxRequestMatcher"/>

  <bean id="loginEntryPoint"
        class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <constructor-arg value="/login.jsp" />
  </bean>

  <bean id="ajaxEntryPoint"
        class="com.mycompany.AjaxAuthenticationEntryPoint" />
  
  <bean id="authenticationRequestCache"
        class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
    <property name="requestMatcher" ref="ajaxRequestMatcher" />
  </bean>

  <bean id="authenticationEntryPoint"
        class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
    <constructor-arg>
      <map>
        <entry key-ref="ajaxRequestMatcher" value-ref="ajaxEntryPoint" />
      </map>
    </constructor-arg>
    <property name="defaultEntryPoint" ref="loginEntryPoint" />
  </bean>

  <security:http entry-point-ref="authenticationEntryPoint">
    <security:intercept-url
        access="IS_AUTHENTICATED_REMEMBERED"
        pattern="/secure/**"/>

    <security:form-login
        login-page="/login.jsp"/>
    <security:logout/>

  </security:http>

Java Classes

public class AjaxRequestMatcher implements RequestMatcher {
    public boolean matches(HttpServletRequest request) {
        return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }
}

public class AjaxAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException)
        throws IOException, ServletException {    

        // form json string
        String json = ....

        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setHeader("Cache-control", "no-cache");
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);

        response.getWriter().print(json);
        response.getWriter().flush();
        response.getWriter().close();
    }
}



Monday, September 15, 2014

Spring Security and Active Directory without Manager Username/Password

Recently I run into an issue to integrate an application with corporate LDAP. Typical LDAP requires a bind. If the server does not allow anonymous bind, then a manager/admin username and password must be supplied.

But when dealing with Active Directory, one can use the incoming user and password of an authentication request to do the binding. This is a non-standard way to integrate with a typical LDAP server. Spring Security has direct support for this type of configuration and setup.

<security:authentication-manager alias="authenticationManager">
  <security:authentication-provider ref="adAuthenticationProvider"/>
</security:authentication-manager>

<bean id="adAuthenticationProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
  <constructor-arg value="mycompany.com"/>
  <constructor-arg value="ldap://my-company-active-directory-url"/>
  <property name="useAuthenticationRequestCredentials" value="true"/>
  <property name="convertSubErrorCodesToExceptions" value="true"/>
</bean>

Accessing Http Session In JAX-RS 2.0

JAX-RS 2.0 defines Filters and Interceptors. One can access the Http session by implementing a filter. For example:
import java.io.IOException;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;

@Named
@Provider
public class MyInterceptor implements ContainerRequestFilter {
    @Context
    private HttpServletRequest servletRequest;

    public void filter(ContainerRequestContext requestContext) throws IOException {
        
        HttpSession session = servletRequest.getSession(false);

        if (session != null) {
            // do stuff
        }
    }
}

Friday, September 5, 2014

Gradle and Embedded Jetty access log

The standard Jetty plugin for Gradle has limited features. For example, there seems to have no option to configure access logs with this plugin. Luckily there is a much more feature rich jetty plugin called JettyEclipse for Gradle. This can be found here. Configuring this plugin is quite easy. Add the following into your build.graddle.
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath (group: 'com.sahlbach.gradle', name: 'gradle-jetty-eclipse-plugin', version: '1.9.+')
    }
}
apply plugin: 'jettyEclipse'

jettyEclipse {
    System.setProperty("catalina.home", "${project.buildDir}")   
    requestLog = new File("${project.buildDir}/logs/access.log")
}

Then run gradle clean jettyEclipseRun

Handling Resources with Gradle

Gradle copies resources to a different directory than Maven. So when migrating from Maven, the source code needs to be changed to take care of the new resource locations. In order to keep the resources are copied to the correct location, add the following into your projects build.gradle file.

project.buildDir = 'target'

sourceSets {
    main {
        output.resourcesDir "${project.buildDir}/classes"
    }
    test {
        output.resourcesDir "${project.buildDir}/classes/test"
    }
}

Querydsl JPA Model Generation with Gradle

Querydsl is a very useful tool to write concise JPA queries. This requires a plugin to generate the meta model classes that will be referenced in the queries. There is a maven plugin to handle the code generation. But when using Gradle, this needs to be configured manually. Add this to the build.gradle file.
ext {
    generatedSourcesDir = file("${buildDir}/generated-sources")
    querydslVersion    = "3.4.2"
}
sourceSets {
    main {
        java {
            srcDir "src/main/java"
            srcDir generatedSourcesDir
        }
    }
}
configurations {
    querydslapt
}
task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
    source = sourceSets.main.java
    classpath = configurations.compile + configurations.querydslapt
    options.compilerArgs = [
            "-proc:only",
            "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor"
    ]
    destinationDir = generatedSourcesDir
}
compileJava {
    doFirst {
        generatedSourcesDir.mkdirs();
    }
    options.compilerArgs += ['-s', generatedSourcesDir]

    dependsOn generateQueryDSL
}
dependencies {
      compile  "com.mysema.querydsl:querydsl-core:${querydslVersion}"
      compile  "com.mysema.querydsl:querydsl-jpa:${querydslVersion}"
      querydslapt  "com.mysema.querydsl:querydsl-apt:${querydslVersion}"
}

Wednesday, August 27, 2014

Spring, Spring Data, RestEasy, Apache Shiro and LDAP

Here are the basic configurations needed to integrate Spring, Spring Data, RestEasy, Apache Shiro and LDAP authentication.

web.xml

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

    <!-- LogBack Configuration File  -->
    <context-param>
      <param-name>logbackConfigLocation</param-name>
      <param-value>/WEB-INF/logback.xml</param-value>
    </context-param>

    <!-- Define the spring context locations -->
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/test-service-config.xml</param-value>
    </context-param>

    <!--
        Filters
    -->

    <filter>
      <filter-name>jpaFilter</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetBean</param-name>
      <param-value>jpaFilter</param-value>
    </init-param>
    </filter>

    <filter-mapping>
      <filter-name>jpaFilter</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetBean</param-name>
      <param-value>shiroFilter</param-value>
    </init-param>

    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher> 
    <dispatcher>FORWARD</dispatcher> 
    <dispatcher>INCLUDE</dispatcher> 
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>

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

 
  <listener>
    <listener-class>
      ch.qos.logback.ext.spring.web.LogbackConfigListener
    </listener-class>
  </listener>

  <!-- 
     Servlet
   -->

  <servlet>
    <servlet-name>testServlet</servlet-name>
    <display-name>Test Servlet</display-name>

    <description>
      A Test Servlet
    </description>

    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
 
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        /WEB-INF/test-web-config.xml
      </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>testServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

</web-app>

test-service-config.xml

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:repository="http://www.springframework.org/schema/data/repository"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation=
       "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/jdbc
        http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/data/repository
        http://www.springframework.org/schema/data/repository/spring-repository.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd">

  <context:annotation-config/>
  <context:component-scan base-package="org.suresh.api.rest.v1"/>
  <context:component-scan base-package="org.suresh.service"/>
  <context:component-scan base-package="org.suresh.controllers"/>

  <jpa:repositories base-package="org.suresh.repo"/>
  <jdbc:embedded-database id="testDb" type="HSQL"/>

  <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="testDb"/>
    <property name="persistenceUnitName" value="test"/>
    <property name="packagesToScan">
      <list>
        <value>org.suresh.model</value>
      </list>
    </property>

    <property name="jpaVendorAdapter">
      <bean
          class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="generateDdl" value="true"/>
        <property name="database" value="HSQL"/>
      </bean>
    </property>
  </bean>

  <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    <property name="dataSource" ref="testDb"/>
  </bean>

  <!-- Security -->
  <security:ldap-server
      root="dc=suresh,dc=org"
      ldif="classpath:resources/test-users.ldif" port="33389"/>

  <bean id="shiroSecurityManager"
        class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="ldapRealm"/>
  </bean>

  <bean id="ldapRealm" class="org.apache.shiro.realm.ldap.JndiLdapRealm">
    <property name="userDnTemplate"
              value="uid={0},ou=people,dc=suresh,dc=org"/>
    <property name="contextFactory" ref="ldapContextFactory"/>
  </bean>

  <bean id="ldapContextFactory"
        class="org.apache.shiro.realm.ldap.JndiLdapContextFactory">
    <property name="url"
              value="ldap://localhost:33389/dc=suresh,dc=org"/>
  </bean>

  <bean id="shiroFilter"
        class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="shiroSecurityManager"/>
    <property name="loginUrl"        value="/views/login.jsp"/>
    <property name="successUrl"      value="/views/home.jsp"/>

    <property name="filterChainDefinitions">
      <value>
        /api/**   = authc
        /views/** = authc
      </value>
    </property>

  </bean>

  <!-- enable shiro annotations -->
  <bean id="lifecycleBeanPostProcessor"
        class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

  <bean
      class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
      <property name="securityManager" ref="shiroSecurityManager"/>
    </bean>

  <!-- Filters -->
  <bean id="jpaFilter"
        class="org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter"/>
</beans>

test-web-config.xml

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation=
       "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

  <import resource="classpath:springmvc-resteasy.xml"/>

  <context:annotation-config/>
  <context:component-scan base-package="org.suresh.api.rest.v1"/>

  <mvc:annotation-driven/>


  <mvc:resources mapping="/images/**" location="/public/images/" order="-1"/>
  <mvc:resources mapping="/stylesheets/**"
                 location="/public/stylesheets/"    order="-1"/>
  <mvc:resources mapping="/javascripts/**"
                 location="/public/javascripts/"     order="-1"/>
  <mvc:default-servlet-handler/>


  <!-- RESTEasy prefix -->
  <bean id="resteasy.handlerMapping" parent="abstract.resteasy.handlerMapping">
    <property name="prefix" value="/api" />
  </bean>
</beans>