Typically you define some intercept-url patterns to configure access to pages with spring security
<http use-expressions="true">
<intercept-url pattern="/**/secure/**" access="hasRole('ROLE_SECURE_USER')" />
...
</http>
We now have pages with url's that are not known beforehand. But we can write a piece of code to decide whether a specific page should be protected or not, i.e. we can provide a service that returns true if the page has to be protected. So what we'd like to do is something like this:
<http use-expressions="true">
<intercept decide="@service.mustProtect()" access="hasRole('ROLE_SECURE_USER')" />
...
</http>
How can this be achieved with Spring? Do we have to write a custom filter? How would you implement such a filter?
Actually, it was quite easy to solve our problem by injecting a custom filter just before the FilterSecurityInterceptor
. You can then throw an AccessDeniedException
in the filter's doFilter
method to trigger authentication.
Spring security config:
<http use-expressions="true">
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="accessFilter"/>
...
</http>
<beans:bean id="accessFilter" class="xyz.AccessFilter" />
Filter:
public class AccessFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!currentUserCanAccessPage(request)) {
throw new AccessDeniedException();
}
chain.doFilter(request,response)
}
private boolean currentUserCanAccessPage(ServletRequest request) {
//implement
}
}
What the <intercept-url>
tags do is simply populating a repository (called SecurityMetadataSource
) where RequestMatcher
s are mapped to ConfigAttribute
s. RequestMatcher
s are generated based on the pattern
attribute, while ConfigAttribute
s are just holding the string specified in the access
attribute.
When an incoming request hits the FilterSecurityInterceptor
filter, it will iterate over the list of these mappings to find the first entry, where the RequestMatcher
indicates a match, in order to determine what kind of access restrictions it has to enforce (described by the mapped ConfigAttribute
).
Now, if you could put your own RequestMatcher
implementation to this map, your requirement would be basically solved. The difficulty is that the namespace configuration does not cater for this use-case, it's only able to interpret the pattern
attribute either as AntPathRequestMatcher
or as RegexRequestMatcher
.
This means that you will have to configure the security infrastructure at the bean level, because the <http>
element creates its own FilterSecurityInterceptor
that cannot be replaced.
In this article you can find great help on how to write this kind of manual security configuration.