A search for meaning in software and life
RSS icon Email icon Home icon
  • Securing a GWT Grails app with Shiro

    Posted on January 27th, 2010 Peter 3 comments

    A recent poster on the Grails user mailing list asked how to secure a GWT application using Shiro. The Shiro Plugin is mostly designed to work with Grails controllers and GSPs, so that’s what the current documentation targets. GWT is a different kettle of fish, so can the Shiro Plugin help here?

    As it happens, I needed to handle unauthenticated GWT requests myself recently. Here’s the solution I came up with.

    Imagine we have a GWT-RPC request come in to the server, but the session has timed out. First off, is the request protected? That depends on how you have your security filters configured. I currently have this:

    class SecurityFilters {
        def filters = {
            ...
            gwt(controller: "gwt", action: "index") {
                before = {
                    accessControl { true }
                }
            }
            ...
        }
    }
    

    Because I’m protecting the “gwt” controller, all GWT-RPC requests require an authenticated (or remembered) user. That means whenever the current user’s session times out, Shiro kicks in and asks for authentication. It does this by redirecting to the login page, but this doesn’t work within the context of a GWT-RPC request. So what can you do?

    The Shiro plugin allows you to customise the behaviour when the user requires authentication. Simply implement the following method in your filters class:

    class SecurityFilters {
        ...
        def onNotAuthenticated(subject, filter) {
            if (filter.request.contentType?.startsWith("text/x-gwt-rpc")) {
                filter.render(text: "", status: 401)
                return false
            }
            else {
                // Not an AJAX request, so continue with the default behaviour.
                return true
            }
        }
    }
    

    If this method is implemented, the plugin calls it whenever authentication is required. In this particular case, I return a status of 401 (Unauthorized) if the request is of type GWT-RPC. Otherwise, I trigger the default behaviour (redirection to the login page) by returning true. In other words, the return value of the method determines whether the default behaviour occurs or not.

    Note The return value of onNotAuthenticated() only has an effect in version 1.1-SNAPSHOT and greater of the plugin.

    That’s it for the server side. All we need to do now is handle the 401 response on the client. How you exactly do this depends on your approach to GWT requests, but the basic mechanics are the same. I use the action handler mechanism supported by the plugin, along with Google Gin, so I can simply wrap the standard action service with a version that handles 401 responses:

    public class SecureActionService implements GwtActionServiceAsync {
        private final GwtActionServiceAsync actionService;
    
        @Inject
        SecureActionService(final GwtActionServiceAsync actionService) {
            this.actionService = actionService;
        }   
    
        public <T extends Response> void execute(final Action<T> action, final AsyncCallback<T> callback) {
            actionService.execute(action, new AsyncCallback<T>() {
                public void onFailure(Throwable caught) {
                    if (caught instanceof StatusCodeException &&
                            ((StatusCodeException) caught).getStatusCode() == 401) {
                        // Authentication/authorisation failure. Assume
                        // authentication at this stage and redirect to
                        // login page.
                        final String loginUrl = GWT.getHostPageBaseURL() + "login";
                        Window.open(loginUrl, "_self", "");
                    }
                    else {
                        callback.onFailure(caught);
                    }
                }
    
                public void onSuccess(T result) {
                    callback.onSuccess(result);
                }
            });
        }
    }
    

    Now all the client code that has the action service injected gets the custom secure service instead. This particular implementation isn’t ideal because it redirects to the standard login page for a 401, whereas it would probably be better to have a simple login popup that then resends the action on successful authentication. However, the key point is that you need to implement the onFailure() method and check for a StatusCodeException. You can then do whatever you want with a 401 response.

    The solution outlined above is fairly basic, particularly as it doesn’t deal with unauthorised (as opposed to unauthenticated) access, but it should provide you with a suitable starting point to implement your own solution. You can also see how powerful the command pattern is combined with the dependency injection provided by Google Gin.

    One last thing to mention: all the above is geared towards GWT-RPC. If you prefer to use JSON for the client-server communication, then you’ll need a different approach. I have no experience yet on that side of things, but you should be able to check the HTTP headers in onNotAuthenticated to determine whether a request is from GWT or not. Alternatively, simply check for the JSON content type.

     

    3 responses to “Securing a GWT Grails app with Shiro”

    1. Peter, thanks a lot for this. I’ve already seen myself end up in parsing the message body of the exception trying to find out what happened…

    2. This is a great post. I will use your example to handle AJAX session timeouts in our app. Thank you Peter!

    3. Hi Peter

      in your opinion, do you have a prefered Security solution to use with Grails?
      – Spring Security plugin (http://www.grails.org/plugin/acegi)
      – Shiro Plugin (http://www.grails.org/plugin/shiro)
      – Authentication Plugin (http://grails.org/plugin/authentication)
      - “home made”
      – any other?

      Tks
      Felipe

    Leave a reply