Grails Acegi Plugin and Securing Multiple Resources using Basic Authentication

Grails is pretty awesome. Not only does it use Groovy as its main language, but it also provides nice DSL access to Spring settings.

Securing a website is something that can often be fairly complex and easy to get wrong. Grails provides a plugin for the state-of-the-art security model Spring Security (also called Acegi) (plugin can be found here).

Everything was all well and good until I tried to use two different security models simultaneously: a form-based login for human beings and an http basic authentication login for machines consuming an API.

This turned out to be pretty hard. The plugin requires setting the following in the resources.groovy file to work with basic authentication at all:

beans = {
  authenticationEntryPoint(org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint) {
    realmName = 'Grails Realm'
  }
}

(The plugin is supposed to automatically do this by setting the basicProcessingFilter = true flag in the SecurityConfigure.groovy file, but there is a bug as of version 0.5.2 which is, as of now, the latest production release.) This worked for basic authentication, but would no longer redirect users to the form-based login.

Back to the drawing board.

What I discovered was that I could change between using the form-based login or the http headers authentication but not use them both. (I should clarify this by saying that I could shove the headers into the request and it would work, but a challenge response of 401 was not being sent back to the client which technically breaks the http authentication spec.) I found this thread (with contributions from the author of the Acegi plugin itself) but, while it pointed me in a nice direction, it didn’t answer the question fully. What I really needed was a way to use the URL to decide how to authenticate.

I finally discovered that the ExceptionTranslationFilter was not unique for the URLs I was using. In short, if the URL looked like: http://www.test.com/application/api/user/show/1, I wanted to use basic authentication and if the URL was http://www.test.com/application/user/show/1, I wanted to use the form-based authentication. The redirect is controlled by the ExceptionTranslationFilter and there was only one (so only one model would work at a time).

All I ended up having to do is create my own ExceptionTranslationFilter and wire it to the API URL. The final code in resources.groovy looks as follows:

import org.springframework.security.util.FilterChainProxy
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsAccessDeniedHandlerImpl
import org.springframework.security.ui.ExceptionTranslationFilter

// Place your Spring DSL code here
beans = {
  basicAuthenticationEntryPoint(org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint) {
    realmName = 'Grails Realm'
  }

  basicExceptionTranslationFilter(ExceptionTranslationFilter) {
    authenticationEntryPoint = ref('basicAuthenticationEntryPoint')
    accessDeniedHandler = ref('accessDeniedHandler')
    portResolver = ref('portResolver')
  }

  springSecurityFilterChain(FilterChainProxy) {
    filterInvocationDefinitionSource = """
         CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
         PATTERN_TYPE_APACHE_ANT
         /xml*/**=authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor 
         /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
         """
  }
}

Now, visiting any URL that prefixed by a /xml/ will be a call to the basic authentication headers and every other prefix will be a standard form-based login.

Since I shaved a serious yak figuring this out, I hope it helps someone!

About these ads

About johnnywey

Welcome to A Regular Expression. This blog is designed to reflect my thoughts on life, music, software design, Apple, faith, philosophy, and whatever else I can think of.

Posted on October 29, 2009, in Grails, Java, Programming. Bookmark the permalink. 9 Comments.

  1. I have suffered lots of pain due to the plugin and I will soon remove it to setup Spring Security myself. It is pretty easy to do and one is finally in full control.

  2. You’re a champ – thanks for posting this. It’s so discouraging to try to learn a new framework just to discover that everything is broken

  3. Wow… big thanks for this!!! I have one little problem left… i canĀ“t get the basic http login dialog anymore. Just the Tomcat Message 401 message “This request requires HTTP authentication ()”. Any hint how to fix this?

    Best regard…

    • Hi Rouven,

      Make sure you have your filterInvocationDefinitionSource setup properly for the URL pattern you want basic authentication to work on.

      In the above case, it looks like:
      /xml*/**=authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor

      This is the URL pattern that will process your basic authentication including the correct login dialog (which is a function of http and not created by your application).

      Hope that helps!

  4. Thanks for this post, probably saved me some hours to figure out myself. BTW, the referenced bean name for authenticationEntryPoint is wrong. It should be “basicProcessingFilterEntryPoint” instead of “basicAuthenticationEntryPoint”.

  5. Thanks, indeed!

    I just wanted to ask you to clarify something, if you’d oblige.
    In the beginning of your post, you write:

    “In short, if the URL looked like: http://www.test.com/application/api/user/show/1, I wanted to use basic authentication and if the URL was http://www.test.com/application/user/show/1, I wanted to use the form-based authentication.”

    Later, you write:
    “Now, visiting any URL that prefixed by a /xml/ will be a call to the basic authentication headers and every other prefix will be a standard form-based login.”

    I am confused as to how your final URLs look like. Can you show us an example.

    Thanks again.

    Daniel

  6. Thanks a lot for your post! I had the same problem using Spring Security plugin, and after a little “translation” i managed to port your solution.

  7. Great post. I’m attempting to do this with Grails 1.3.7 but the imports that you specified for the resources.groovy file aren’t being resolved. I installed the plugin via ‘grails install-plugin’ – my plugin versions are:

    spring-security-core 1.2.1 — Spring Security Core Plugin
    spring-security-ui 0.1.2 — User interface extensions for the Spring Security plugin.

    Is this how you installed the plugin and if so, was it necessary to do any setup (moving Jar files around) to get your solution above to work?

    Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: