You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用拦截式Servlet Filter触发登录弹窗?Java Web应用URL保护

Great question! Implementing this kind of protected resource access without disrupting the parent page is totally doable with a Servlet Filter paired with frontend handling. Let's walk through the solution step by step:

1. Create the Servlet Filter

First, we'll build a Filter that targets all /private/* resources. The core logic here is to check for a logged-in user in the session; if missing, return a 401 Unauthorized response instead of redirecting (which would break the parent page).

@WebFilter("/private/*")
public class PrivateResourceFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpReq = (HttpServletRequest) request;
        HttpServletResponse httpResp = (HttpServletResponse) response;

        // Check if a logged-in user exists in the session
        Object loggedInUser = httpReq.getSession().getAttribute("loggedInUser");
        
        if (loggedInUser != null) {
            // User is authenticated: pass the request through
            chain.doFilter(request, response);
        } else {
            // User not logged in or session expired: return 401 without redirecting
            httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            // Optional: send a JSON message to help frontend handle the error
            httpResp.setContentType("application/json");
            httpResp.getWriter().write("{\"error\":\"Authentication required to access this resource\"}");
            
            // Prevent browser's default auth popup by setting a custom WWW-Authenticate header
            httpResp.setHeader("WWW-Authenticate", "Custom");
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}
}

2. Frontend: Intercept 401 Responses and Show Login Modal

Since we're returning a 401 instead of redirecting, we need frontend code to catch this status code and trigger your custom login popup. This works best if all requests to /private/* are made via AJAX/fetch (so the browser doesn't handle the 401 natively).

Here's a global fetch interceptor to handle this:

// Override the default fetch to intercept responses
const originalFetch = window.fetch;
window.fetch = async function(url, options) {
    const response = await originalFetch(url, options);
    
    // Check if the request was to a private resource and returned 401
    if (response.status === 401 && url.includes('/private/')) {
        // Trigger your custom login modal
        showLoginPopup();
        // Return a modified response to prevent default error handling
        return new Response(JSON.stringify({unauthorized: true}), {status: 401});
    }
    
    return response;
};

// Example function to show your login modal (adjust to match your UI)
function showLoginPopup() {
    // If using Bootstrap modals:
    const loginModal = new bootstrap.Modal(document.getElementById('loginModal'));
    loginModal.show();
    
    // For a custom modal, replace with your own logic:
    // document.getElementById('loginModal').style.display = 'block';
}

3. Handle Login Success and Retry Requests

After the user logs in successfully, you'll want to re-fetch the private resource that triggered the 401, so the parent page updates without reloading.

First, the login form handler:

document.getElementById('loginForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    
    const formData = new FormData(e.target);
    const loginResponse = await fetch('/login', {
        method: 'POST',
        body: formData
    });
    
    if (loginResponse.ok) {
        // Close the login modal
        const loginModal = bootstrap.Modal.getInstance(document.getElementById('loginModal'));
        loginModal.hide();
        
        // Retry the last failed private resource request (optional)
        // You can store the failed request details before showing the modal
        retryLastPrivateRequest();
    } else {
        alert('Login failed! Please check your credentials.');
    }
});

// Example: Store and retry the failed request
let lastFailedRequest = null;

// Modify the fetch interceptor to store failed requests:
window.fetch = async function(url, options) {
    const response = await originalFetch(url, options);
    
    if (response.status === 401 && url.includes('/private/')) {
        lastFailedRequest = {url, options};
        showLoginPopup();
        return new Response(JSON.stringify({unauthorized: true}), {status: 401});
    }
    
    return response;
};

function retryLastPrivateRequest() {
    if (lastFailedRequest) {
        fetch(lastFailedRequest.url, lastFailedRequest.options)
            .then(res => res.json())
            .then(data => {
                // Update the parent page with the fetched data
                updatePrivateContent(data);
                lastFailedRequest = null;
            });
    }
}

function updatePrivateContent(data) {
    // Replace with your logic to update the page's private section
    document.getElementById('privateContentContainer').textContent = JSON.stringify(data);
}

Key Notes

  • Avoid redirects/forwards: Using a 401 response ensures the parent page's URL and state stay intact. Redirecting would break the user's current context.
  • Session expiration handling: When the session expires, session.getAttribute("loggedInUser") returns null, so the Filter automatically treats it as an unauthenticated request—no extra code needed.
  • Async requests only: If users navigate directly to /private/* via a link, the browser will handle the 401 natively (showing its own auth popup). To prevent this, ensure all private resource access is done via AJAX/fetch, or add a check on initial page load to redirect to the homepage if unauthenticated (but that's a separate flow).

内容的提问来源于stack exchange,提问作者R. Dafoe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:23:37