Monday, February 02, 2015

How to get the original client IP address when it is masked in the Http request by a load-balancer/proxy in a Spring web application...

It's quite common when apache web servers are behind a load balancer/proxy, the actual client IP address is replaced with that of the balancer/proxy. In those situations, the servers could be configured to replace the actual IP address of the client in the HTTP request with that of the proxy server and put the original IP address in a special HTTP request header "X-Forwarded-For". The header gets passed downstream to the apache web server and to the app server like Tomcat and would actually be available to the application via the HTTP header. Apache and Tomcat can as well be configured to log this original IP address.

If there is a need for the application to log the original IP address, the spring security logs the authentication details in DEBUG mode, anyway. But in this case, the IP address that gets logged by spring security will be the proxy IP address as it extracts this detail from the HTTP request that was already modified by the proxy server.

So, in order to log the original client IP Address, a custom authentication details class needs to be written and hooked into the spring security appropriately. The following are the two Spring security classes that are involved in this:

org.springframework.security.web.authentication.WebAuthenticationDetailsSource - it is a simple class which build thes authentication details object from the HTTPServletRequest
org.springframework.security.web.authentication.WebAuthenticationDetails - is the authentication details class that holds the remoteAddress and sessionId.

The two custom classes needed can be as simple as and similar to the above two. All you need to do is to correctly set the remoteAddress extracting it from the HTTP request header instead of from the HTTP Request.

Below are the two custom classes written in groovy. Java is no different, but will be little more code implementing the equals and hashCode methods. The import statements are omitted for brevity.

Custom Classes:
/**
 * CustomWebAuthenticationDetails.groovy
 * A custom WebAuthenticationDetails object to look at the X-FORWARDED-FOR
 * header to get the source remote IP address in case of load-balancer/proxy
 * which sets the source IP address in HTTP header and masks it in the actual
 * HTTPRequest.
 */
@EqualsAndHashCode
public class CustomWebAuthenticationDetails implements Serializable {
    private static final long serialVersionUID =
        SpringSecurityCoreVersion.SERIAL_VERSION_UID

    private final String remoteAddress
    private final String sessionId

    /**
     * Records the remote address and will also set the session Id if a session
     * already exists (it won't create one).
     *
     * @param request that the authentication request was received from
     */
    public CustomWebAuthenticationDetails(HttpServletRequest request) {
        //get remoteAddress if set in the header. otherwise, get it from the request
        this.remoteAddress = request.getHeader('X-FORWARDED-FOR') ?: request.getRemoteAddr()

        HttpSession session = request.getSession(false)
        this.sessionId = session ? session.getId() : null
    }

    /** @see java.lang.Object#toString() */
    public String toString() {
        "${super.toString()}: RemoteIpAddress: ${remoteAddress};SessionId:${sessionId}"
    }
}

/**
 * CustomWebAuthenticationDetailsSource.groovy
 */
public class CustomWebAuthenticationDetailsSource
    implements AuthenticationDetailsSource<HttpServletRequest, CustomWebAuthenticationDetails> {

    /**
     * @see org.springframework.security.web.authentication.WebAuthenticationDetailsSource#buildDetails(javax.servlet.http.HttpServletRequest)
     */
    @Override
    public CustomWebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new CustomWebAuthenticationDetails(context)
    }
}

Spring Configuration:
Configure Spring secuirty to use CustomWebAuthenticationDetailsSource class instead of it's default WebAuthenticationDetailsSource
    <sec:form-login
        login-page="/"
        default-target-url="/home"
        always-use-default-target="true"
        authentication-details-source-ref="customWebAuthenticationDetailsSource"
        authentication-failure-url="/?login_error=true"/>
    <sec:anonymous enabled="false" />

    <bean id="customWebAuthenticationDetailsSource"
        class="com.giri.security.CustomWebAuthenticationDetailsSource"/>

Testing
When you use FireBug to test and examine the headers of the HTTP request, the "X-Forwarded-For" will not be seen as the browser request will not contain any trace of this. Both the request and header get modified by the apache server behind the load-balancer/proxy server and hence it can only be accessed from that point onwards, but not from the browser initiated request.

Wednesday, December 17, 2014

Leverage Spring JavaMailSender's batch send method and gain performance when sending series of emails...

I have recently experienced a performance issue in a module that is coded to send email notifications to multiple recipients using Spring Java Mail support. The email text for each recipient was different and hence it was initially coded to loop through all recipients, prepare MimeMessage with the help of an anonymous inner class of MimeMessagePreparator for each recipient, and call JavaMailSender's send(MimeMessagePreparator mimeMessagePreparator) method. For every message the send method was taking about 20 secs or so for the call to finish. When there about 20 recipients, there were 20 such calls to this method and the delay was noticeable in minutes.

While reading through the Javadoc of Spring classes, I found that there is another send method in JavaMailSender class which takes an array of MimeMessages as an argument and sends mail in batch mode. I used this method instead and prepared an array of MimeMessages one per each recipient using just the MimeMessageHelper. This improved the performance and for 20 recipients which is 20 emails anyway, it only took about 20 seconds.

Under the covers this method could be using the same mail session to send all MimeMessages in batch mode.

Monday, December 08, 2014

SpringOne2GX-2014 Day-4

Day-4 Sessions

Reactor and Reactive Streams - John Maldini, Stephan Brisbin
Reactor and Reactive Streams - John Maldini, Stephan Brisbin
My notes
  • NanoService, MicroService, NotTooBigService
  • Reactors are going to help across the boundaries, where microservices cross boundaries to interact with other services in order to get the work done. Microservice can be a method call, a db operation, a file operation etc. which has it’s own (thread) boundary.
  • Reactive Programming- Event driven, scalable, low-latency, fault-tolerant
  • Scaling doesn’t mean throwing more and more resources, often times more throughput can be achieved by scaling down the big operations into smaller so that they can go in and out quickly and better throughput can be achieved with smaller number of threads with less latency.
Groovy vampires, Combining Groovy, REST, NoSQL, and more - Kenneth Kousen

 My notes
  • Making Java Groovy Book: Ratpack next edition
  • REST    Addressible resources, Uniform interface (most public facing restful api are restricted to GET requests, read-only web-service), Content Negotiation (content type in url:e.g. JSON, or accept-header, POST req: both request-header and content-header to indicate what you are sending and what you can receive)
  • GET requests in Groovy are trivial: ‘...url...’.toURL().text()
  • s = [k1:v1, k2:v2].collect{k,v-> “$k=$v”}.join(‘&’)
  • s = [k1:v1, k2:v2].collect{it}.join(‘&’) //more groovier
  • JsonOutput.prettyPrint(url.toUrl().text())
    • commands (interactive javascript based console): mongo, show databases, use movies, show collections,
    • Java driver available com.mongodb.BasicDBObject
    • TODO: GMongo Project (Groovy) - take a look at the code, @delegate Mongo mongo, delegates to Java Mongo class
    • GMongo mongo = new GMongo(); def db = db.getdb(‘myDb’); db.myCollection.find()....//etc
    • db.myCollection << collection //populates mongo db collection, if not there it creates and populates
    • mongo db generates ids
  • Parsing JSON and converting to Java Objects
  • GSON, Google JSON to convert JSON to Java Object
  • Groovy is dynamically typed, but strongly typed language. JSON has no type, it’s weakly typed language.
  • Lazybones - Generates project templates
  • gvm : gvm c //gvm current
  • lazybones list //list all types of apps it can generate templates for, ratpack is one in the list
  • TIP:@Singleton : Groovy
  • TIP: @Shared : JUnit and Spock, if you don’t want vars to be re-initialized for each test…
  • TIP:groovy each vs. every: each returns a collection, every returns a boolean that satifies the statement in the closure
  • Grails: Alternative server, REST capabilities, Mongo plugin

Android and Groovy - a winning pair- Cedric Champeau

Android and Groovy - a winning pair- Cedric Champeau
 My notes
  • Why Android? Uses JVM, SDK is free, Tooling is freely available (Android Studio), don’t own a Mac, Swift (Apple) - inspired by Groovy, Scala and many languages
  • Why Groovy: Great features in the language and in the API, Built on top of Giant Java, Android developers shouldn’t be suffering, Java on Android is very verbose, incredibly verbose, tonnes of ceremony
  • Groovy on Android: the problems- Groovy is dynamic, lot of things happen at run-time, Intensive use of reflection, Potentially slow invocation paths, bytecode on Android is different. Multiple runtimes on Android: Dalvik, ART (new: your device becomes a compiler for the application, it compiles into native bytecode, application installation takes time, purely native code and there is no JVM on the device, so the app runs faster), Behavior not the same as the standard JVM
  • Discobot: 2 guys started Groovy on Android in 2011, forking Groovy 1.7. Groovy was not modularized at the time
  • Dex files: Dalvik VM is new bytecode format
  • Groovy 2.4: Android objectives- Main focus on @CompileStatic, Optional use of dynamic Groovy, Support Android in std distribution, Building a full Android application in Groovy, Bring the goodness of Groovy to Android, Opening new doors: Invent new frameworks.
  • Groovy for Android
    • Requirements: Gradle, Android Studio, Groovy 2.4.0-beta-3
    • Groovy 2.4 Android Support: Must use a specific Android jar, replaces java.beans with openbeans, reduced number of methods for 64k limit of dex files, workarounds for Android specific behavior
    • Gradle plugin: groovy-android plugin
    • Demo: an application on Android in Groovy which starts a countdown timer on Android device and syncs up the timer on Android wearable device (watch)
    • Demo: Create an app- Android studio, create project (mobile and wear), add needed stuff to build.gradle, rename the Java file to Groovy, make the code Groovy
  • TIP: @ClosureParams - with this param, you tell the compiler what type of parameter to infer when parameters are passed into closure without have to define the parameter types.
    • Groovifying Android APIs
    • Performance: GR8Conf Agenda app- Groovy jar 4.5MB, appl size: 2MB, After ProGuard only 1MB, 8.2MB of RAM (but lots of images). There is absolutely no difference from Java with the groovy app @CompilerStatic
    • Other Cool things:
      • SwissKnife - @OnClick(R.id.button) kind of method injections - all such tools are based on APT Annotation Processing Tools. In Groovy it’s all through AST
      • Grooid Tools: Builders for Views, experimental
      • Dragger-like dependency injection FW
      • Data binding APIs
      • Improved reactive APIs
  • Future is now: NYT - Next app will be written in Groovy
http://www.infoq.com/presentations/android-groovy-jvm?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term=global

Takeaways for me:
  • Spring Boot
  • Java 8
  • Groovy 2.4 beta 3 for Android
  • Grails 2.3
  • Spock
This was my 4th Spring One conference, my first one was in 2006 and then I attended the ones in 2012 and 2013. There were about 1000+ attendees and it must have been the largest gathering ever, I guess. The food was good, speakers were great and it was a great great experience. I was totally in my professional world for 4 days and enjoyed every bit of it. Looking forward to more and more in future ;)

Have fun with SPRING Frameworks and Groovy! ;)