resthub / springmvc-router

Adds route mapping capacity to any "Spring MVC based" webapp. Uses playframework.org Router implementation.

Home Page:http://resthub.github.com/springmvc-router/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible improvements for integration with Spring Security

electrotype opened this issue · comments

I use Spring Security and I'm now integrating springmvc-router with it. It works well, but I think it could be improved to fit even better!

The main point is that my security context is declared way before my mvc context (which contain the org.resthub.web.springmvc.router.RouterHandlerMapping bean). The result is that the "Router" object has to be initialized manually before the security context can use it.

Here's what I do :

  • Before the security context is loaded :

<bean id="routesFileList" class="java.util.ArrayList">
    <constructor-arg>
         <list>
            <value>classpath:routes.conf</value>
        </list>
    </constructor-arg>
</bean>
<bean id="servletPrefix" class="java.lang.String">
    <constructor-arg value="" />
</bean>
<bean id="reverser" class="com.xxx.util.beanutils.ContextReverseRouter" init-method="init">
    <constructor-arg ref="routesFileList" />
    <constructor-arg ref="servletPrefix" />
</bean>

  • ContextReverseRoute.java. Its purpose is to :
  1. Initialize the Router object
  2. Being a way to do reverse routing in the context files (mainly for the security context!)

public class ContextReverseRouter extends WebApplicationObjectSupport
{
    List<String> routeFiles;
    String servletPrefix;

    public ContextReverseRouter(List<String> routeFiles, String servletPrefix)
    {
        this.routeFiles = routeFiles;
        this.servletPrefix = servletPrefix;     
    }

    public void init()
    {
        try 
        {
            List<Resource> fileResources = new ArrayList<Resource>(); 
            for(String fileName : this.routeFiles) 
            {
                fileResources.addAll(Arrays.asList(getApplicationContext().getResources(fileName)));
            }
            // Initialize the Router
            Router.load(fileResources, this.servletPrefix);
        } 
        catch (IOException e) 
        {
            throw new RouteFileParsingException("Could not read route configuration files", e);
        }
    }

    public static String reverse(String action, String mustBe)
    {
        if(mustBe == null)
        {
            throw new RuntimeException("'mustBe' can't be null");
        }

        ActionDefinition actionDefinition = Router.reverse(action);

        if(!mustBe.equals(actionDefinition.url))
        {
            throw new RuntimeException("The generated reverse route for '" + action + "', " + actionDefinition.url + " , is different from the expected mustBe : " + mustBe);
        }

        return actionDefinition.url;
    }
}

(Note that I currently only need top-level urls to be reverse-routed in my security file, and no need for reverse() methods with key/value parameters.)

  • Extract of my security context :

<sec:http auto-config="true" use-expressions="true">
    <sec:intercept-url pattern="#{reverser.reverse('adminController.index', '/admin')}/**" access="hasRole('#{constants.getConstant('com.xxx.model.user.SecurityRole$SecurityRoleEnum.ADMIN')}')" />
(...)

(I also have a "constants" bean to access constants, but that's another story)

  • Extract of the mvc context :

<bean id="handlerMapping" class="org.resthub.web.springmvc.router.RouterHandlerMapping">
    <property name="order" value="0" />

    <!-- use the same parameters as our "reverser" ContextReverseRouter -->
    <property name="routeFiles" ref="routesFileList" />
    <property name="servletPrefix" ref="servletPrefix" />

    <property name="interceptors">
        (...)
    </property>
</bean>

As you can see, I added a second parameter to the reverse() method, "mustBe", to make sure that, if I change (and screw!) something in the routes.conf file, my pages will still be protected as they should or otherwise an exception will be thrown... But I guess not everybody would need this feature.

All this seems to work great! So why do I say that some improvements could be done to springmvc-router? Well, The Router has to be initialized twice : once by my ContextReverseRouter and once by the RouterHandlerMapping object. I also have to use the "routeFiles" and "servletPrefix" at two places. All this does not matter that much since it only occures when the application is starting and it is easily managed... But I think your springmvc-router project would be even nicer if it was done out of the box to integrate better with Spring Security!

Thanks again for your work.

To me, reverse routing should only be called within:

  • controllers
  • views
  • IDE tooling

Why are you reloading the routes?
Is the Router empty if you try to call static methods from the Router class within your custom "reverser" class?

Why are you trying to use the Router when using annotations on controllers should be less verbose in that case?

I guess you are right, maybe annotations is better for that. But I wanted to centralize all security rules in one context file instead of having them everywhere... Very similar to why I'm using your springmvc-router actually : to have everything centralized!

But I have to think more about what changes could be made to springmvc-router to make it easier to deal with inside context files... I may create a branch to test some ideas.