Search code examples
hibernatespring-bootgrailsgrails3

Grails - do not open hibernate session for certain request path


We'd like to implement API rate limiting for Grails 3 application. We use interceptor and redis for this purpose. There is however a problem of opening hibernate session (which opens database connection) for each request coming to Grails. This is a resource consumption we cannot neglect as it can easily reach MySQL connection limits with simple attack.

The question is what is the best way how to force Grails not to open hibernate session for certain urls/interceptors. I am aware of API gateways like konghq.com which is not an option for us.

I also know about GrailsOpenSessionInViewInterceptor implementing OpenSessionInViewInterceptor which is responsible for session management. So is it the only one option to override this interceptor? How would you open hibernate sessions for those request complying with rate limits?


Solution

  • Finaly, I override GrailsOpenSessionInViewInterceptor where I open hibernate session only if rate limits are satisfied.

    import org.grails.web.servlet.mvc.GrailsWebRequest
    import org.grails.web.util.GrailsApplicationAttributes
    import org.springframework.web.context.request.WebRequest
    
    import javax.servlet.http.HttpServletRequest
    
    class MyOpenSessionInViewInterceptor extends GrailsOpenSessionInViewInterceptor {
    
        @Override
        void preHandle(WebRequest request) throws DataAccessException {
            GrailsWebRequest grailsRequest = (GrailsWebRequest) request.getAttribute(GrailsApplicationAttributes.WEB_REQUEST,
                    WebRequest.SCOPE_REQUEST)
            HttpServletRequest servletRequest = grailsRequest.request
    
            // intercept /api/* requests
            if (!servletRequest.requestURI.startsWith('/api')) {
                // if rate limits exceeded
                if (checkRateLimits) {
                    servletRequest.setAttribute('MY_RATE_LIMITS_EXCEEDED', true)
                    return
                }
    
                super.preHandle(request)
            }
        }
    }
    

    Don't forget to inject your bean in resource.groovy.

    // Place your Spring DSL code here
    beans = {
    
        // intercept opening of hibernate cache
        openSessionInViewInterceptor(MyOpenSessionInViewInterceptor) {
            hibernateDatastore = ref('hibernateDatastore')
        }
    }
    

    I put this together with in house grails interceptor where I check passed attribute from Spring open session interceptor.

    class V1RateLimitInterceptor {
    
        V1RateLimitInterceptor() {
            match(namespace: 'api')
        }
    
        boolean before() {
            // request rate limits
            boolean rateLimit = (boolean) request.getAttribute('MY_RATE_LIMITS_EXCEEDED')
            if (rateLimit) {
                return false
            }
    
            return true
        }
    }
    

    This is however a temporary solution for us as we would use some API gateway like konghq.com in future.