Having a multi-tenancy application, I want to end any request without a tenant very early.
I run spring boot (web) with spring session and spring security.
In my case, I have several strategies to determine the tenant, TenantFromX, TenantFromY .. all running on order Ordered.HIGHEST_PRECEDENCE
- so as early as possible.
On order Ordered.HIGHEST_PRECEDENCE+1
I run the TenantMustExistFilter
filter to validate, that any strategy actually did match / find a tenant definition
@Log4j2
@Component
@RequiredArgsConstructor
// After TenantFromX(min) but before SessionRepositoryFilter(Session) which is min+50
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class TenantMandatoryFilter extends GenericFilterBean
{
@Override
public void doFilter(
ServletRequest request, ServletResponse response,
FilterChain filterChain
) throws ServletException, IOException
{
if (TenantStore.getInitMode().isEmpty()) {
throw new NoTenantException("No tenant identified for request - request blocked");
}
try {
filterChain.doFilter(request, response);
} finally {
TenantStore.clear();
}
}
}
Eventhough I throw an Runtime exception NoTenantException
all other filters after that filter are called anyway.
For example SessionRepositoryFilter
is called on Ordered.HIGHEST_PRECEDENCE+50
and then the spring security FilterProxyChain
is triggered.
How do I effectively stop the chain after TenantMandatoryFilter
has failed to validate?
I expected either a return;
or and Exception
or just not calling filterChain.doFilter(request, response);
to be just enough to end the chain execution.
Do I have a misunderstanding of the process or the idea here?
I can successfully end the chain by replacing
if (TenantStore.getInitMode().isEmpty()) {
throw new NoTenantException("No tenant identified for request - request blocked");
}
with
if (TenantStore.getInitMode().isEmpty()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
Interestingly the below will not work, even though it seems semantically more correct. It does not work means, it will call the SessionRepositoryFilter
and the FilterChainProxy
after that nevertheless.
if (TenantStore.getInitMode().isEmpty()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid tenant");
return;
}
Which I assume is since sendError
sets the response to suspended to complete any other chain (which is what I do not want).
It still does not seem right, so how to do this the right way? Or is this a conceptual flaw altogether?
Sending an error, either via an explicit call or by throwing an exception, will cause the request to be dispatched to the application’s error page handling which will invoke the filters again for that dispatch. Setting the status is the right thing to do.