Search code examples
grailsshiro

How do I get Shiro's annotations to work in Grails?


I'm trying to apply annotation-based security to my Grails app.

Peter says here that we should be using Shiro's annotations instead of the quasi-deprecated grails-shiro plugin annotations.

How does one get that working?

I'm finding the grails-shiro plugin strays from how Shiro does things, what with the Realm-converting and all. Has anyone tried implementing Shiro directly rather than using the grails plugin? Any success?

Thanks, Graham.


Solution

  • G'day I have taken over the Grails-Shiro plugin project. I'm currently re-writing the functional tests and can confirm that the shiro annotations do work, with a couple of caveats:

    • You can use them in controllers on method actions in grails 2.x
    • You can use them on Service methods
    • They don't currently work on service classes (I'm investigating this)

    e.g. this works on a service

    class SecuredMethodsService {
    
    def methodOne() {
        return 'one'
    }
    
    @RequiresGuest
    def methodTwo() {
        return 'two'
    }
    
    @RequiresUser
    def methodThree() {
        return 'three'
    }
    
    @RequiresAuthentication
    def methodFour() {
        return 'four'
    }
    
    @RequiresRoles('User')
    def methodFive() {
        return 'five'
    }
    
    @RequiresPermissions("book:view")
    def methodSix() {
        return 'six'
    }
    

    }

    or in a controller on an action method like this:

        @RequiresAuthentication
    def unrestricted() {
        render(view: 'simple', model: [msg: "secure action"])
    }
    

    When using annotations you may need to add an "afterView" filter to catch the AuthorizationException thrown by the annotation e.g.

    class ShiroSecurityFilters {
    def filters = {
        all(uri: "/**") {
            before = {
                // Ignore direct views (e.g. the default main index page).
                if (!controllerName) return true
                // Access control by convention.
                accessControl()
            }
            afterView = { e ->
                while (e && !(e instanceof AuthorizationException)) {
                    e = e.cause
                }
    
                if (e instanceof AuthorizationException) {
                    if (e instanceof UnauthenticatedException) {
                        // User is not authenticated, so redirect to the login page.
                        flash.message = "You need to be logged in to continue."
                        redirect(
                                controller: 'auth',
                                action: 'login',
                                params: [targetUri: request.forwardURI - request.contextPath])
                    } else {
                        redirect(controller: 'auth', action: 'unauthorized')
                    }
                }
    
            }
        }
    }
    

    }

    I hope that helps. A new version of the plugin should be released RSN.

    Cheers, Peter.