1
votes

My grails web app has two parts: one for desktop / laptop browsers, one for mobile devices which uses jquery-mobile. The mobile part lives in a subtree /mobile/*. Using spring security I'd like to have different controllers/views for login, logout, etc. I haven't found any useful hints researching this topic on the web.

The only option I can currently think of is to extract the mobile app into a grails project of its own which will then force me to extract common logic into a grails plugin which will then force me to a completely different dev and deployment set up and so on... I'd much rather keep mobile and non-mobile part in the same app but cannot figure out how.

Any suggestions appreciated...

3

3 Answers

2
votes

I would do one of these two options:

  • Have a default controller which will redirect to mobileLogin or login controller based on client browser (or something else)
  • Use one login controller but customise display with CSS (and then you can do redirection to desktop/mobile controllers if you have to in your defaultTargetUrl)
1
votes

Have you looked at the spring-mobile plugin?

Allows you to do something like the following in your controllers where you need to switch between standard and mobile.

def login() {
    def view = 'login'

    withMobileDevice {
      view = 'mobile/login'
    }

    render view: view
}
1
votes

Try this one again...

I have been working on something similar lately as well. And had the same 'wants' as you. Here is what I ended up with.

To point to different login screens I overwrote the AuthenticationEntryPoint step of the security filter chain (spring security). I used the same logic that the spring-mobile plugin uses. (In fact, you will have to have spring-mobile plugin installed for this to work) The deviceResolver is wired up by that plugin.

package com.myapp.security

import org.codehaus.groovy.grails.plugins.springsecurity.AjaxAwareAuthenticationEntryPoint
import javax.servlet.http.HttpServletRequestimport javax.servlet.http.HttpServletResponse
import org.springframework.security.core.AuthenticationException

class MyAppAuthenticationEntryPoint extends AjaxAwareAuthenticationEntryPoint {
    def mobileLoginFormUrl
    def deviceResolver

    @Override
    protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
        if (deviceResolver.resolveDevice(request).isMobile())
        {
             return mobileLoginFormUrl
        }
        return super.determineUrlToUseForThisRequest(request, response, e)
    }
}

Wired like so in resources.groovy

authenticationEntryPoint(com.myapp.security.MyAppAuthenticationEntryPoint) {
    loginFormUrl = conf.auth.loginFormUrl
    forceHttps = conf.auth.forceHttps
    ajaxLoginFormUrl = conf.auth.ajaxLoginFormUrl
    useForward = conf.auth.useForward
    portMapper = ref('portMapper')
    portResolver = ref('portResolver')
    deviceResolver = ref('deviceResolver')
    mobileLoginFormUrl = conf.auth.mobileLoginFormUrl
}

Config lines in Config.groovy

grails.plugins.springsecurity.auth.loginFormUrl = '/register'
grails.plugins.springsecurity.auth.mobileLoginFormUrl = '/mobile/login'

I have also written my AuthenticationSuccessHandler step to force mobile users to the mobile landing page after login.

package com.myapp.security

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.codehaus.groovy.grails.plugins.springsecurity.AjaxAwareAuthenticationSuccessHandler
import org.springframework.security.web.savedrequest.RequestCache

class MyAppAuthenticationSuccessHandler extends AjaxAwareAuthenticationSuccessHandler {
    def mobileTargetUrl
    def deviceResolver
    RequestCache requestCache

    @Override
    protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
         if (isMobile(request))
         {
             return mobileTargetUrl
         }
         return super.determineTargetUrl(request, response)
    }

    @Override
    void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.Authentication authentication) {        
        if (isMobile(request))
        {
            // we always want to go to the mobile landing page here.
            requestCache.removeRequest(request, response);
        }
        super.onAuthenticationSuccess(request, response, authentication)
    }

    private boolean isMobile(request) {
        deviceResolver.resolveDevice(request).isMobile()
    }

    @Override
    void setRequestCache(RequestCache requestCache) {
        super.setRequestCache(requestCache)
        this.requestCache = requestCache
    }

}

This doesn't stop the user from browsing to a non-mobile page, but it does force them to the /mobile/index after login. Form there all my links on my mobile pages refer to other mobile pages.