Scenario Let's say we have two end points which are ["/api/v1/resource","/api/v1/resource/admin"]. Also I have two filters [F1,F2]. I have configured two SecurityFilterChain [SFC1,SFC2], the first one has F1 in the filters and the second one has F2 in the filters. I want to be able to secure the first end point with [SFC1] and secure the second end point with [SFC1,SFC2].

Expected Behavior Spring security pick up and apply multiple SecurityFilterChain.

Current Behavior The current behavior is that spring try to match request url with configured [SecurityFilterChain]s, the first one win, that mean I can apply only one SecurityFilterChain. The current behavior that I found is in [FilterChainProxy] class in (getFilters) method while debugging

I am trying to apply multiple [SecurityFilterChain]s at the same time as per configuration

Question Is there any role or restriction that lead to current implementation? I went through document but with no benefit.

Comment From: marcusdacoregio

Hi, @aliShreef.

That is the expected behavior. Only one SecurityFilterChain will be applied to a request. Can you clarify what you are trying to achieve with this?

Comment From: aliShreef

Hi @marcusdacoregio

As I explain in scenario, I want to apply one SecurityFilterChain for specific end point and for other end point I want to apply same SecurityFilterChain plus another SecurityFilterChain which contain extra filter which do some specific logic for that end point.

Why it is implemented that way, "first match win"? and why we can't implement the other way that we can make us apply multiple SecurityFilterChain?

Current implementation [FilterChainProxy.class]

private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
        HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
        List<Filter> filters = this.getFilters((HttpServletRequest)firewallRequest);
        if (filters != null && filters.size() != 0) {
            if (logger.isDebugEnabled()) {
                logger.debug(LogMessage.of(() -> {
                    return "Securing " + requestLine(firewallRequest);
                }));
            }

            FilterChainProxy.VirtualFilterChain virtualFilterChain = new FilterChainProxy.VirtualFilterChain(firewallRequest, chain, filters);
            virtualFilterChain.doFilter(firewallRequest, firewallResponse);
        } else {
            if (logger.isTraceEnabled()) {
                logger.trace(LogMessage.of(() -> {
                    return "No security for " + requestLine(firewallRequest);
                }));
            }

            firewallRequest.reset();
            chain.doFilter(firewallRequest, firewallResponse);
        }
    }

private List<Filter> getFilters(HttpServletRequest request) {
        int count = 0;
        Iterator var3 = this.filterChains.iterator();

        SecurityFilterChain chain;
        do {
            if (!var3.hasNext()) {
                return null;
            }

            chain = (SecurityFilterChain)var3.next();
            if (logger.isTraceEnabled()) {
                ++count;
                logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, count, this.filterChains.size()));
            }
        } while(!chain.matches(request));

        return chain.getFilters();
    }

Suggested Implementation

private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, 
    ServletException {
        FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
        HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
        List<SecurityFilterChain> securityFilterChain = this.getFilters((HttpServletRequest)firewallRequest);
        if (securityFilterChain != null && securityFilterChain.size() != 0) {
            if (logger.isDebugEnabled()) {
                logger.debug(LogMessage.of(() -> {
                    return "Securing " + requestLine(firewallRequest);
                }));
            }
            Iterator var1 = securityFilterChain.iterator();
            while (var1.hasNext()){
                SecurityFilterChain var2 = (SecurityFilterChain) var1.next();
                List<Filter> var3 = var2.getFilters();
                FilterChainProxy.VirtualFilterChain virtualFilterChain = new FilterChainProxy.VirtualFilterChain(firewallRequest, chain, 
    var3);
                virtualFilterChain.doFilter(firewallRequest, firewallResponse);
            }


        } else {
            if (logger.isTraceEnabled()) {
                logger.trace(LogMessage.of(() -> {
                    return "No security for " + requestLine(firewallRequest);
                }));
            }

            firewallRequest.reset();
            chain.doFilter(firewallRequest, firewallResponse);
        }
    }

    private List<SecurityFilterChain> getFilters(HttpServletRequest request) {
        Iterator var3 = this.filterChains.iterator();

        List<SecurityFilterChain> chains = Collections.emptyList();
        while(var3.hasNext()){
            SecurityFilterChain var4 = (SecurityFilterChain)var3.next();
            if(var4.matches(request)){
                chains.add(var4);
            }
        }
        return chains.size() == 0 ?null:chains;
    }

This suggestion is not the best but it explain the idea for what I am try to achieve.

Also can you answer the question "Is there any role or restriction that lead to current implementation?".

Comment From: marcusdacoregio

It sounds to me that what you want to achieve is to apply both filters in one filter chain. If you have F1 in one SFC1, and F2 in the SFC2, why don't you also add F1 in SFC2? Did you take a look at the SecurityFilterChain docs?

Running two SecurityFilterChain for the same request would mean that we would run a lot of filters twice with no need.

Is there any role or restriction that lead to current implementation? I went through document but with no benefit.

I don't think I follow your question.

Can you share your code or a minimal, reproducible sample of what you are trying to achieve?

Comment From: aliShreef

You are right. That sound good and fit my need to add F1 to SFC1 and add F1,F2 to SFC2.

Regarding running two SecurityFilterChain can lead to run some filter twice, Yes in one case if both SFC have same default configuration which come from spring out of the box. But let's say I make one SFC have default configuration which by default has some default filter [HeaderWriterFilter,SessionFilter,...] and for the second SFC disable all default filter come from spring security and add my own custom filter. In that case the filters will not run twice, right?

Regarding my question, it's seem to me that the strategy that implemented by spring which are first match win because of running some filter twice as you mention. But if I made as I mention in second paragraph it will not running filter twice.

Comment From: marcusdacoregio

...and for the second SFC disable all default filter come from spring security and add my own custom filter. In that case the filters will not run twice, right?

Why don't just add the filter to your SFC instead of creating another empty one?

Yes, the first SecurityFilterChain that matches the request is chosen, this is expected.

I'll close this because I feel that this type of question is more suitable for Stackoverflow.