The requirements for our custom filter are the following:
- You can only have one instance of a page
- If you create a new tab and enter the same page, you will not be able to access the page again and you will be redirected to a custom page
- If you create a new browser, same result
- If you create close and open the same browser, same result
- If you refresh the same tab, same result
- You can only gain access if you manually log-out
- You can only gain access if you clear your browser's cache
- You can only gain access if your session has expired
If you need to learn more about the FilterChainProxy and ordering of the aliases, please check the Spring Security Reference Table 2.1. Standard Filter Aliases and Ordering
The format for adding the filter is similar to the following:
Before we start adding real code, let's declare a pseudocode first:
if user has logged-in for the first time flag the session that the user has logged-in if the user tries to log-in again check if there's a protected URL if yes, redirect user to a custom page if no, then proceed with the remaining filtersAnd here's the actual implementation for this filter.
This filter extends OncePerRequestFilter and overrides the doFilterInternal() method
What is OncePerRequestFilter?
Filter base class that guarantees to be just executed once per request, on any servlet container. It provides a doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) method with HttpServletRequest and HttpServletResponse arguments.How did we came up with this implementation? I'm sure it didn't just came out from thin air. Actually, I had to study how the CONCURRENT_SESSION_FILTER alias is implemented. It's filter class is based on ConcurrentSessionFilter.
The getAlreadyFilteredAttributeName() method determines how to identify that a request is already filtered. The default implementation is based on the configured name of the concrete filter instance.
Source: Spring Reference 3 for OncePerRequestFilter
What is ConcurrentSessionFilter?
Filter required by concurrent session handling package.Let's examine the doFilter() method of this class.
This filter performs two functions. First, it calls SessionRegistry.refreshLastRequest(String) for each request so that registered sessions always have a correct "last update" date/time. Second, it retrieves a SessionInformation from the SessionRegistry for each request and checks if the session has been marked as expired. If it has been marked as expired, the configured logout handlers will be called (as happens with LogoutFilter), typically to invalidate the session. A redirect to the expiredURL specified will be performed, and the session invalidation will cause an HttpSessionDestroyedEvent to be published via the HttpSessionEventPublisher registered in web.xml.
Source: Spring Security 3 Reference for ConcurrentSessionFilter
This ConcurrentSessionFilter does the following steps:
1. Retrieve the current session
2. Check if session is not null
3. Check if the SessionInformation is not null
4. Check if the session has expired. If expired, logout and redirect
5. If not expired, continue with remaining filters.
Let's compare this with our custom filter SingleEntryFilter.
Inside the doFilterInternal() method, we do the following:
1. Retrieve the current session and verify if it's not null
2. Retrieve the SessionInformation and verify if it's not null
3. Retrieve the session attribute hasLoggedIn and check if it's null or not
If hasLoggedIn is not null, this means the user has logged already.
4. Next, we loop the guardURI list and check if there's a protected URL. If there's one, we redirect to the redirectURI
5. If this is the user's first time to access the site (i.e new session), set the hasLoggedIn attribute
6. Finally, continue with the remaining filters
Examine how this code resembles the ConcurrentSessionFilter
Now, let's update the the Spring Security XML configuration.
The changes we did in this file is we added the following custom filter:
We also declared the singleEntryFilter bean.
Notice the guardURI is a list. That means we can protect multiple URIs. Cool :)
We then need to update our login controller so that we can see the invalid login alert.
To access the site, please login first by entering the following URL:
http://localhost:8080/spring-security-single-login-filter/krams/auth/loginThen access the following URL:
http://localhost:8080/spring-security-single-login-filter/krams/main/commonYou can only access the URL once! Here's what you'll get if you try again:
That's it. We've just finished implementing our custom filter based on the requirements we laid earlier. They key action here is we studied the existing filters to see how Spring Security does things. I believe this is a good way to enhance one's knowledge of this framework.
Please read the tutorial Spring Security 3 - MVC: Querying the SessionRegistry for a complete step-by-step guide for the other parts of this application.
The best way to learn further is to try the actual application.
Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring3-security-mvc-integration-tutorial/
You can download the project as a Maven build. Look for the spring-security-single-login-filter.zip in the Download sections.
You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run
Share the joy:
Subscribe by reader Subscribe by email Share