Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 217 posts at DZone. You can read more from them at their website. View Full User Profile

Once Roughhousers, Vaadin and Spring Security Can Finally Get Along

08.27.2012
| 3581 views |
  • submit to reddit

Along with Apache Shiro, Spring Security is one of the two most-used security components in the Java world. Using Spring Security with Vaadin needs a little work. In this article, I will show you how you can adapt your Vaadin application to play nice with Spring Security.

Credits go to Henri Sara of the Vaadin team who provided his valuable insight.
Requirements: this article assumes you know some Spring Security, and it uses advanced Vaadin navigator concept.

Nominal uses of Spring Security mandate for the use of subcontexts in the webapp. Each one can then be configured for different access levels. For example, /public is accessible with anonymous access, while /private needs some specific authorizations. Unfortunately, Vaadin doesn't work that way: views are translated into fragments, not subcontexts. From this point on, there are two options: Either tweak Vaadin to use subcontexts, or embed Spring Security inside our application. We will use the latter.

  • The first step is to create a Login form view that sends login events.
  • The root subscribes to login events and handles authentication attemps through a dedicated authentication handler.
  • The handler has to be passed to the login, the password and the http request. Since Vaadin hides the latter in the API, we have to create a special servlet that stores it in a thread local with the help of a utility class:
    public class RequestHolderApplicationServlet extends ApplicationServlet {
     
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
            RequestHolder.setRequest(request);
     
            super.service(request, response);
     
            // We remove the request from the thread local, there's no reason to keep it once the work is done
            RequestHolder.clean();
        }
    }
  • The login handler uses the Spring Security API to create the username/password token needed by the framework. Then it gets the authentication manager from the Spring context and calls the relevant method, delegating real authentication to the configured backend. Last but not least, we set authentication data into the Spring Security context for latter uses.
    public class AuthenticationService {
     
        public void handleAuthentication(String login, String password, HttpServletRequest httpRequest) {
             
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(login, password);
     
            token.setDetails(new WebAuthenticationDetails(httpRequest));
     
            ServletContext servletContext = httpRequest.getSession().getServletContext();
     
            WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
     
            AuthenticationManager authManager = wac.getBean(AuthenticationManager.class);
     
            Authentication authentication = authManager.authenticate(token);
     
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    }
  • Don't forget to also create and clean the Spring Security context. Since we already developed a custom servlet, we'll update it.
    public class RequestHolderApplicationServlet extends ApplicationServlet {
     
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
            SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
     
            RequestHolder.setRequest(request);
     
            super.service(request, response);
     
            RequestHolder.clean();
     
            SecurityContextHolder.clearContext();
        }
    }
Now, the root just has to try/catch the authentication results and navigate to the desired view if succesful. Job complete!

But wait; what if the user knows about the view's name and types the fragment directly into his or her browser's URL bar? No login event, no authentication: he or she will have access directly to the main view, regardless of his or her credentials.

So, there's one last step to implement: we have to bind all our views into a navigator and let the latter handle navigation. Besides, we register it as a ViewChangeListener that will check for credentials before changing the view.

public class ViewChangeSecurityChecker implements ViewChangeListener {
 
    @Override
    public boolean isViewChangeAllowed(ViewChangeEvent event) {
 
        if (event.getNewView() instanceof LoginView) {
 
            return true;
        }
 
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
 
        return authentication == null ? false : authentication.isAuthenticated();
    }
 
    @Override
    public void navigatorViewChanged(ViewChangeEvent event) {}
}
Now, when an unauthenticated user directly types the view name as a fragment, there won't be any action. Notice that the login view has to be accessible in any case.

Note that the above code only checks for an authenticated status, not for a credential one. You can easily enhance it to do just that by forking the GitHub repo.

Original article: http://morevaadin.com/content/spring-security-integration

 

 

Published at DZone with permission of Nicolas Frankel, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)