In my previous blog post about Spring Security, I wrote about filter-based security and how to use that in your Vaadin applications. In this blog post, we are going to look at another way of utilizing Spring Security.
In addition to filters, Spring Security has other ways of protecting your application. In the backend layer, the most commonly used one is method level security. For every secure class, you declare what permissions or roles the current user must have to be able to invoke a method.
During runtime, the secured bean will actually be proxied and an interceptor will perform the authorization before letting the invocation proceed to the real object. This is important to remember when working with Vaadin.
In a Vaadin application, UI components can also be Spring managed beans. Technically, you can therefore apply method level security to your Vaadin components. This is not a good idea since Vaadin does not play nicely with proxied components. Although it might sometimes work, you might end up getting strange client side error notifications when a server-side component and its client-side widget fail to find each other because of the proxy.
On the other hand, method level security in the backend layer will work very well with your Vaadin application and that is something we are going to utilize in the hybrid approach to using Spring Security.
Introducing the hybrid approach
The general idea behind the hybrid approach is to handle the UI layer security yourself and let Spring Security worry about the security from the backend and onwards using method level security. This will give your users a better user experience since everything, including login and logout, will be handled inside your Vaadin application. There are no redirects or auto-generated error pages to deal with. In the next sections, we are going to look at all the things you have to handle yourself when Spring Security is no longer used in the UI layer.
As before, you can find working code samples on GitHub.
The Spring Security configuration for the hybrid approach is a lot simpler than for the filter approach. Web security should be completely disabled for the application. Global method security should be enabled and the authentication manager should be properly configured. That’s basically it and it is demonstrated in the sample code.
Managing the security context
Since there is no filter that will manage the security context, you have to do it yourself. This is actually pretty easy. You have to implement your own
SecurityContextHolderStrategy and plug it into the
SecurityContextHolder when the application starts up. The strategy would then access the current
VaadinSession to store and retrieve the security context.
By managing the security context in this way and storing it in the Vaadin session as opposed to the HTTP session, you can use any supported Vaadin transport protocol including websockets. The security context will also be available inside all
UI.access() calls, even when initiated from a background thread.
However, this is also the biggest security risk since the security context is the key to your backend. You have to be careful you are not accidentally leaking the security context to other sessions - or other threads, for that matter.
Performing the actual authentication is still done by Spring Security, but your application will have to implement the login form and the logic for showing or hiding it. You can do this in the following way:
- In the
init(...) method of your UI, you need to check whether the current user is authenticated or not. You can do this by examining the current
- If authentication is required, you set the content of your UI to your login form. If not, you set the content to your main screen.
- When the user attempts to login, you send an authentication token (e.g. a
UsernamePasswordAuthenticationToken) to the
AuthenticationManager. If an exception is thrown, the authentication has failed and you show an error message. Otherwise, you save the authentication token in the current
SecurityContext and replace the content of your UI with your main screen.
This approach also makes it easy to write applications where some features do not require authentication and others do. You could just pop up your own login window whenever authentication is needed, without having to redirect to another URL and then back.
Please note, however, that Spring Security’s remember me authentication does not work with this approach since it relies heavily on HTTP requests. If you want to support remember me, you have to implement it more or less from scratch.
Session fixation protection
A common practice to protect against session fixation attacks is to create a new session immediately after a user has been authenticated and invalidate the old one. Vaadin has a method for doing just this that you can invoke after the user has been authenticated, but before you store the authentication token in the
Unfortunately, there is a catch: this will most likely not work if you are using websockets. Fortunately, there are at least three different ways around this:
- If you are using Vaadin 7.6 or newer, you can use the new
WEBSOCKET_XHR transport that was already mentioned earlier in this article.
- Disable push completely in the login page (i.e. don’t put
@Push on your UI) and programmatically enable it after the user has logged on. This approach is demonstrated in the sample code.
- Use a push mechanism that uses basic HTTP.
Protecting your views
Protecting views and hiding or showing components based on the current user’s permissions have to be completely handled by the Vaadin application (but this was also the case when using filter-based security). How exactly this is done depends on the requirements. One way could be to query the granted authorities of the current user, for example in a helper method.
If the Navigation API is used, a custom
ViewAccessControl can be used to grant or deny access to individual views. The
SpringViewProvider will automatically pick up and use a Spring bean that implements this interface. It will be queried before any view is created and returned by the view provider. If access to a view is denied, you can choose to either act like the view did not exist in the first place (the default behavior) or you can plug in an access denied view that is shown to the user. This is demonstrated in the sample code.
As mentioned before, you should not use method level security on your UI components because of the proxies. If you are reusing any of the Spring security annotations on your views, make sure that the method level security configuration ignores your UI classes.
Ideally, your application’s UI should be designed in such a way that any forbidden actions are not even available to the user. However, there may still be cases where a forbidden operation is performed and your application will need to handle these appropriately. The easiest way of doing this is to override the default error handler in your UI. Depending on how you set up your application, you should at least be able to recognize and react to the
AccessDeniedException, possible also to the
AuthenticationException. This is demonstrated in the sample code.
Finally, when the user is done, you have to handle logging out. This is also one of the more critical parts of the application since it should be absolutely impossible to get back into the application without authenticating after logout. There can not be any references to the security context laying around.
How you handle logout depends on your requirements. The simplest way is to just kill the session and reload the page, as demonstrated in the sample code. However, if you have an application where the user can continue to use some features unauthenticated, you have to handle this in some other way. Also think about how you are going to deal with multiple UI instances being open at the same time. What will happen with the other browser windows when you log out? The sample application will simply reload the page, showing the login form again in all the browser windows.