Custom Access Rules for Spring Security

2019-05-10 08:31发布

问题:

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?

回答1:

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
    }
}


回答2:

What the <intercept-url> tags do is simply populating a repository (called SecurityMetadataSource) where RequestMatchers are mapped to ConfigAttributes. RequestMatchers are generated based on the pattern attribute, while ConfigAttributes 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.