Showing posts with label grails. Show all posts
Showing posts with label grails. Show all posts

Monday, 10 August 2015

Adding a logout listener in Grails and spring-security-core

Recently I've had to log certain activities the users were performing in an application. One particular action was logging out.
I'm using spring-security-core plugin to handle authentication and security.
I have done this in the past so I thought I better document this in a way that I don't have to dig up my old code from the annals of... well ~\dev\archived.

Taking advantage of Listeners

I don't want this cluttering my application logic and since Grails is a spring application, I turned my attention to ApplicationListeners. You could argue that I could have made use of filters but if the logout handler already emits an event when a user is successfully logged out, why not use it?
So, lets start our (short) implementation of this. First you will have to tell spring-security to publish http session events so that you can catch them later. In order to do so, add the following line to your grails-app/conf/Config.groovy:
grails.plugin.springsecurity.useHttpSessionEventPublisher = true
Now you have to implement your listener: import org.springframework.context.ApplicationListener import org.springframework.security.core.session.SessionDestroyedEvent public class MyLogoutEventListener implements ApplicationListener<SessionDestroyedEvent> { @Override public void onApplicationEvent(SessionDestroyedEvent event){ /*Your code here*/ } } (If you're not sure where to put it, place it in your src/groovy/ directory)
And then you need to register this bean in grails-app/conf/spring/resources.groovy beans = { myLogoutEventListener(MyLogoutEventListener) } Note: Your MyLogoutEventListener will listen for any SessionDestroyedEvent which means that when someone logs in, the listener will trigger as the anonymous session has been destroyed, so remember to check if the session is anonymous.

Thursday, 6 August 2015

Tomcat session mix-up hell (aka, RTFM)

Weirdness!

Recently I encountered a very strange issue where I would be logged out of an application in one tab if I opened a different instance of the same application (running on a different port but on the same machine) on another tab.

For clarity, this was my environment:
  • localhost
    • http://localhost:7224 → Application A1 on Tomcat T1 running with Java J1
    • http://localhost:8080 → Application A2 on Tomcat T2 running with Java J2

And what happened was this:
  1. Open A1 in Tab1
  2. Open A2 in Tab2
  3. Go to Tab1 and login to A1
  4. Go to Tab2 and refresh
  5. Go to Tab1 and refresh
  6. I am logged out from Tab1

WHAT?! But... how?

I am running two different instances on two different Tomcat containers using two different Javas?! Is there a leak somewhere? Unless... the problem is with the client. No. It couldn't be... could it?
I tried on other devices, other OSs, and other browsers... same thing. Ok, this must be something the server is sending back.

How I cursed at my computer.

Overnight I kept thinking about this and I realised I had this problem in my previous company. It wasn't the same but it was slightly similar but I remembered it had something to do with cookies.
Cookies are domain-specific (I was using localhost for both), this means they wouldn't care about ports. This was a good clue

The next morning I checked my StackOverflow question and Emmanuel Rosa had the courtesy of reminding my that Grails (well, the container) uses cookies to store session IDs.

RIGHT!

With Chrome, if you open Developer Tools → Resources Tab → Cookies you should see this:


According to the Tomcat 8 documentation there's an attribute called sessionCookieName which could be used to solve this.

So, how to fix it?

To address this issue, you'd have to make both apps stop competing for the JSESSIONID name. For Tomcat 8, if you look in {tomcat}/conf/context.xml the <Context> node is probably empty. This makes the session cookie name default to SESSIONID. The solution is to explicitly set sessionCookieName. After you do that change you should see something like below: <?xml version='1.0' encoding='utf-8'?> <Context sessionCookieName="APPNAMESESSIONID8080"> <WatchedResource>WEB-INF/web.xml>/WatchedResource> <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource> </Context> And you'd make a similar change for the other application: <?xml version='1.0' encoding='utf-8'?> <Context sessionCookieName="APPNAMESESSIONID7224"> <WatchedResource>WEB-INF/web.xml>/WatchedResource> <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource> </Context> I used DASHBOARDSESSIONID<portname> (as you can see in the screenshot below)
If you have another look at your developer tools, you should see that the issue is now gone (you may see the left-over JSESSIONID but that is ok. Your application will ignore it).