private void filter(final HttpServletRequest request,
                        final HttpServletResponse response,
                        final FilterChain chain)
            throws IOException, ServletException {

        LOGGER.debug(() ->
                LogUtil.message("Filtering request uri: {}, servletPath: {}, servletName: {}",
                        request.getRequestURI(),
                        request.getServletPath(),
                        NullSafe.get(request.getHttpServletMapping(), HttpServletMapping::getServletName)));

        // Log the request for debug purposes.
        RequestLog.log(request);

        final String servletPath = request.getServletPath();
        final String fullPath = request.getRequestURI();
        final String servletName = NullSafe.get(request.getHttpServletMapping(), HttpServletMapping::getServletName);

        if (request.getMethod().equalsIgnoreCase(HttpMethod.OPTIONS)) {
            // We need to allow CORS preflight requests
            LOGGER.debug("Passing on OPTIONS request to next filter, servletName: {}, fullPath: {}, servletPath: {}",
                    servletName, fullPath, servletPath);
            chain.doFilter(request, response);
        } else if (isStaticResource(fullPath, servletPath)) {
            LOGGER.debug("Static content, servletName: {}, fullPath: {}, servletPath: {}",
                    servletName, fullPath, servletPath);
            chain.doFilter(request, response);
        } else {
            LOGGER.debug(() -> LogUtil.message("Session ID {}, request URI {}",
                    Optional.ofNullable(request.getSession(false))
                            .map(HttpSession::getId)
                            .orElse("-"),
                    request.getRequestURI() + Optional.ofNullable(request.getQueryString())
                            .map(str -> "/" + str)
                            .orElse("")));

            Optional<UserIdentity> optUserIdentity;

            // Api requests that are not from the front-end should have a token.
            // Also request from an AWS ALB will have an ALB signed token containing the claims
            // Need to do this first, so we get a fresh token from AWS ALB rather than using a stale
            // one from session.
            optUserIdentity = openIdManager.loginWithRequestToken(request);
            if (LOGGER.isDebugEnabled()) {
                logUserIdentityToDebug(
                        optUserIdentity, fullPath, "after trying to login with request token");
            }

            // If no user from header token, see if we have one in session already.
            optUserIdentity = openIdManager.getOrSetSessionUser(request, optUserIdentity);
            if (LOGGER.isDebugEnabled()) {
                logUserIdentityToDebug(optUserIdentity, fullPath, "from session");
            }

            if (optUserIdentity.isPresent()) {
                final UserIdentity userIdentity = optUserIdentity.get();
                LOGGER.debug(() -> LogUtil.message("Setting user in session, user: {} {}, path: {}",
                        userIdentity.getClass().getSimpleName(),
                        userIdentity,
                        fullPath));
                // Set the identity in session if we have a session and cookie
                if (UserIdentitySessionUtil.requestHasSessionCookie(request)) {
                    UserIdentitySessionUtil.set(request, userIdentity);
                }

                // Now handle the request as this user
                securityContext.asUser(userIdentity, () ->
                        process(request, response, chain));

            } else if (shouldBypassAuthentication(request, fullPath, servletPath, servletName)) {
                LOGGER.debug("Running as proc user for unauthenticated servletName: {}, " +
                             "fullPath: {}, servletPath: {}", servletName, fullPath, servletPath);
                // Some paths don't need authentication. If that is the case then proceed as proc user.
                securityContext.asProcessingUser(() ->
                        process(request, response, chain));

            } else if (isApiRequest(servletPath)) {
                // If we couldn't log in with a token or couldn't get a token then error as this is an API call
                // or no login flow is possible/expected.
                LOGGER.debug("No user identity so responding with UNAUTHORIZED for servletName: {}, " +
                             "fullPath: {}, servletPath: {}", servletName, fullPath, servletPath);
                response.setStatus(Response.Status.UNAUTHORIZED.getStatusCode());

            } else {
                // No identity found and not an unauthenticated servlet/api so assume it is
                // a UI request. Thus instigate an OpenID authentication flow
                try {
                    final String postAuthRedirectUri = getPostAuthRedirectUri(request);

                    final String code = UrlUtils.getLastParam(request, OpenId.CODE);
                    final String stateId = UrlUtils.getLastParam(request, OpenId.STATE);
                    final String redirectUri = openIdManager.redirect(request, code, stateId, postAuthRedirectUri);
                    LOGGER.debug("Code flow UI request so redirecting to IDP, " +
                                 "redirectUri: {}, postAuthRedirectUri: {}, path: {}",
                            redirectUri, postAuthRedirectUri, fullPath);

                    // HTTP 1.1.
                    response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
                    // HTTP 1.0.
                    response.setHeader("Pragma", "no-cache");
                    // Proxies.
                    response.setHeader("Expires", "0");

                    response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
                    response.setHeader("Location", redirectUri);

                } catch (final RuntimeException e) {
                    LOGGER.error(e.getMessage(), e);
                    throw e;
                }
            }
        }
    }
Filter to avoid posts to the wrong place (e.g. the root of the app)
stroom-security/stroom-security-impl/src/main/java/stroom/security/impl/SecurityFilter.java