CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-javax-servlet--javax-servlet-api

Java Servlet API specification defining core interfaces and classes for web application development

Pending
Overview
Eval results
Files

security-filtering.mddocs/

Security and Filtering

The Java Servlet API provides a comprehensive security framework and filtering system. This includes request/response filtering, security constraints, authentication mechanisms, authorization controls, and transport security.

Filter Interface and Chain

/**
 * Core interface for implementing servlet filters.
 * Filters are invoked before and after servlet processing.
 */
public interface Filter {
    
    /**
     * Initialize the filter with configuration parameters.
     * Called once when the filter is loaded.
     */
    void init(FilterConfig filterConfig) throws ServletException;
    
    /**
     * Process the request and response, potentially modifying them.
     * Must call chain.doFilter() to continue processing.
     */
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException;
    
    /**
     * Clean up resources when the filter is destroyed.
     * Called once when the filter is removed from service.
     */
    void destroy();
}

/**
 * Interface representing a chain of filters to be applied to a request.
 */
public interface FilterChain {
    
    /**
     * Continue processing the request through the filter chain.
     * The last filter in the chain will invoke the target servlet.
     */
    void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException;
}

/**
 * Configuration interface providing filter initialization parameters.
 */
public interface FilterConfig {
    
    /**
     * Get the name of this filter instance.
     */
    String getFilterName();
    
    /**
     * Get the servlet context for the web application.
     */
    ServletContext getServletContext();
    
    /**
     * Get the value of a filter initialization parameter.
     */
    String getInitParameter(String name);
    
    /**
     * Get an enumeration of all initialization parameter names.
     */
    Enumeration<String> getInitParameterNames();
}

GenericFilter Base Class

/**
 * Abstract base class providing default implementations for the Filter interface.
 * Similar to GenericServlet, this simplifies filter implementation.
 */
public abstract class GenericFilter implements Filter, FilterConfig, Serializable {
    
    private transient FilterConfig config;
    
    /**
     * Initialize the filter and store the config.
     */
    public void init(FilterConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    
    /**
     * Convenience init method for subclasses to override.
     */
    public void init() throws ServletException {
        // Default implementation does nothing
    }
    
    /**
     * Abstract doFilter method that subclasses must implement.
     */
    public abstract void doFilter(ServletRequest request, ServletResponse response, 
                                 FilterChain chain) throws IOException, ServletException;
    
    // FilterConfig delegation methods
    public String getFilterName() {
        return config.getFilterName();
    }
    
    public ServletContext getServletContext() {
        return config.getServletContext();
    }
    
    public String getInitParameter(String name) {
        return config.getInitParameter(name);
    }
    
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }
    
    public FilterConfig getFilterConfig() {
        return config;
    }
    
    /**
     * Log a message to the servlet context log.
     */
    public void log(String msg) {
        getServletContext().log(getFilterName() + ": " + msg);
    }
    
    public void log(String message, Throwable t) {
        getServletContext().log(getFilterName() + ": " + message, t);
    }
    
    public void destroy() {
        // Default implementation does nothing
    }
}

HttpFilter Base Class

/**
 * Abstract base class for HTTP-specific filters.
 * Extends GenericFilter with HTTP servlet support.
 */
public abstract class HttpFilter extends GenericFilter {
    
    /**
     * Default doFilter implementation that casts to HTTP request/response
     * and delegates to the HTTP-specific doFilter method.
     */
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        if (!(request instanceof HttpServletRequest) || 
            !(response instanceof HttpServletResponse)) {
            throw new ServletException("HttpFilter can only process HTTP requests");
        }
        
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }
    
    /**
     * HTTP-specific doFilter method that subclasses should override.
     * Default implementation just continues the filter chain.
     */
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, 
                           FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }
}

Security Constraint Annotations

@ServletSecurity Annotation

/**
 * Annotation to declare security constraints for a servlet.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServletSecurity {
    
    /**
     * Default HTTP constraint applied to all HTTP methods
     * not explicitly constrained by httpMethodConstraints.
     */
    HttpConstraint value() default @HttpConstraint;
    
    /**
     * Array of HTTP method-specific constraints.
     */
    HttpMethodConstraint[] httpMethodConstraints() default {};
}

/**
 * Annotation representing HTTP security constraints.
 */
@Target({})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HttpConstraint {
    
    /**
     * Semantic to be applied when no roles are specified.
     */
    EmptyRoleSemantic value() default EmptyRoleSemantic.PERMIT;
    
    /**
     * Transport guarantee requirement.
     */
    TransportGuarantee transportGuarantee() default TransportGuarantee.NONE;
    
    /**
     * Array of authorized roles.
     */
    String[] rolesAllowed() default {};
}

/**
 * Annotation for HTTP method-specific security constraints.
 */
@Target({})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HttpMethodConstraint {
    
    /**
     * HTTP method name (GET, POST, etc.).
     */
    String value();
    
    /**
     * Semantic to be applied when no roles are specified.
     */
    EmptyRoleSemantic emptyRoleSemantic() default EmptyRoleSemantic.PERMIT;
    
    /**
     * Transport guarantee requirement.
     */
    TransportGuarantee transportGuarantee() default TransportGuarantee.NONE;
    
    /**
     * Array of authorized roles.
     */
    String[] rolesAllowed() default {};
}

Security Constraint Element Classes

/**
 * Programmatic representation of HTTP constraint configuration.
 */
public class HttpConstraintElement {
    
    private EmptyRoleSemantic emptyRoleSemantic;
    private TransportGuarantee transportGuarantee;
    private String[] rolesAllowed;
    
    /**
     * Create constraint with default values (permit all, no transport guarantee).
     */
    public HttpConstraintElement() {
        this(EmptyRoleSemantic.PERMIT);
    }
    
    /**
     * Create constraint with specified empty role semantic.
     */
    public HttpConstraintElement(EmptyRoleSemantic emptyRoleSemantic) {
        this(emptyRoleSemantic, TransportGuarantee.NONE, (String[]) null);
    }
    
    /**
     * Create constraint with transport guarantee.
     */
    public HttpConstraintElement(TransportGuarantee transportGuarantee, 
                                String... rolesAllowed) {
        this(EmptyRoleSemantic.PERMIT, transportGuarantee, rolesAllowed);
    }
    
    /**
     * Create constraint with all parameters.
     */
    public HttpConstraintElement(EmptyRoleSemantic emptyRoleSemantic,
                                TransportGuarantee transportGuarantee,
                                String... rolesAllowed) {
        if (emptyRoleSemantic == null || transportGuarantee == null) {
            throw new IllegalArgumentException("Null parameters not allowed");
        }
        
        this.emptyRoleSemantic = emptyRoleSemantic;
        this.transportGuarantee = transportGuarantee;
        this.rolesAllowed = rolesAllowed != null ? rolesAllowed.clone() : new String[0];
    }
    
    /**
     * Get the empty role semantic.
     */
    public EmptyRoleSemantic getEmptyRoleSemantic() {
        return emptyRoleSemantic;
    }
    
    /**
     * Get the transport guarantee.
     */
    public TransportGuarantee getTransportGuarantee() {
        return transportGuarantee;
    }
    
    /**
     * Get the array of allowed roles.
     */
    public String[] getRolesAllowed() {
        return rolesAllowed.clone();
    }
}

/**
 * HTTP method-specific constraint element.
 */
public class HttpMethodConstraintElement extends HttpConstraintElement {
    
    private String methodName;
    
    /**
     * Create method constraint with method name.
     */
    public HttpMethodConstraintElement(String methodName) {
        if (methodName == null || methodName.length() == 0) {
            throw new IllegalArgumentException("Method name cannot be null or empty");
        }
        this.methodName = methodName;
    }
    
    /**
     * Create method constraint with method name and constraint.
     */
    public HttpMethodConstraintElement(String methodName, 
                                      HttpConstraintElement constraint) {
        super(constraint.getEmptyRoleSemantic(), 
              constraint.getTransportGuarantee(), 
              constraint.getRolesAllowed());
        
        if (methodName == null || methodName.length() == 0) {
            throw new IllegalArgumentException("Method name cannot be null or empty");
        }
        this.methodName = methodName;
    }
    
    /**
     * Get the HTTP method name.
     */
    public String getMethodName() {
        return methodName;
    }
}

/**
 * Servlet security element combining default and method-specific constraints.
 */
public class ServletSecurityElement extends HttpConstraintElement {
    
    private Collection<HttpMethodConstraintElement> httpMethodConstraints;
    private Collection<String> methodNames;
    
    /**
     * Create servlet security element from annotation.
     */
    public ServletSecurityElement(ServletSecurity annotation) {
        this(annotation.value(), annotation.httpMethodConstraints());
    }
    
    /**
     * Create servlet security element with constraints.
     */
    public ServletSecurityElement(HttpConstraint httpConstraint,
                                 HttpMethodConstraint... httpMethodConstraints) {
        super(httpConstraint.value(), 
              httpConstraint.transportGuarantee(), 
              httpConstraint.rolesAllowed());
        
        this.httpMethodConstraints = new HashSet<>();
        this.methodNames = new HashSet<>();
        
        for (HttpMethodConstraint methodConstraint : httpMethodConstraints) {
            String methodName = methodConstraint.value();
            if (this.methodNames.contains(methodName)) {
                throw new IllegalArgumentException("Duplicate method constraint: " + methodName);
            }
            
            this.methodNames.add(methodName);
            this.httpMethodConstraints.add(new HttpMethodConstraintElement(
                methodName,
                new HttpConstraintElement(methodConstraint.emptyRoleSemantic(),
                                        methodConstraint.transportGuarantee(),
                                        methodConstraint.rolesAllowed())
            ));
        }
    }
    
    /**
     * Get HTTP method constraints.
     */
    public Collection<HttpMethodConstraintElement> getHttpMethodConstraints() {
        return Collections.unmodifiableCollection(httpMethodConstraints);
    }
    
    /**
     * Get method names with specific constraints.
     */
    public Collection<String> getMethodNames() {
        return Collections.unmodifiableCollection(methodNames);
    }
}

Security Enums

/**
 * Enumeration of empty role semantics for security constraints.
 */
public enum EmptyRoleSemantic {
    /**
     * Access is to be denied when no roles are specified.
     */
    DENY,
    
    /**
     * Access is to be permitted when no roles are specified.
     */
    PERMIT
}

/**
 * Enumeration of transport guarantee levels.
 */
public enum TransportGuarantee {
    /**
     * No transport guarantee.
     */
    NONE,
    
    /**
     * Integral transport guarantee (data integrity protection).
     */
    INTEGRAL,
    
    /**
     * Confidential transport guarantee (data confidentiality protection).
     * Typically requires HTTPS.
     */
    CONFIDENTIAL
}

RequestDispatcher Interface

/**
 * Interface for forwarding requests to other resources and including responses.
 */
public interface RequestDispatcher {
    
    // Forward attribute constants
    public static final String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
    public static final String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
    public static final String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
    public static final String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
    public static final String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
    
    // Include attribute constants
    public static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
    public static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
    public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
    public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
    public static final String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
    
    // Error attribute constants
    public static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
    public static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
    public static final String ERROR_MESSAGE = "javax.servlet.error.message";
    public static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
    public static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
    public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
    
    /**
     * Forward the request to another resource.
     * The response must not have been committed.
     */
    void forward(ServletRequest request, ServletResponse response)
        throws ServletException, IOException;
    
    /**
     * Include the response from another resource.
     * The included resource cannot set response status or headers.
     */
    void include(ServletRequest request, ServletResponse response)
        throws ServletException, IOException;
}

Filter Implementation Examples

Authentication Filter

/**
 * Authentication filter that checks for valid user sessions
 */
@WebFilter(
    filterName = "AuthenticationFilter",
    urlPatterns = {"/secure/*", "/admin/*"},
    dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD}
)
public class AuthenticationFilter implements Filter {
    
    private Set<String> excludedPaths;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Initialize excluded paths that don't require authentication
        excludedPaths = new HashSet<>();
        excludedPaths.add("/login");
        excludedPaths.add("/register");
        excludedPaths.add("/public");
        excludedPaths.add("/css");
        excludedPaths.add("/js");
        excludedPaths.add("/images");
        
        String additionalExclusions = filterConfig.getInitParameter("excludedPaths");
        if (additionalExclusions != null) {
            String[] paths = additionalExclusions.split(",");
            for (String path : paths) {
                excludedPaths.add(path.trim());
            }
        }
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        String requestURI = httpRequest.getRequestURI();
        String contextPath = httpRequest.getContextPath();
        String path = requestURI.substring(contextPath.length());
        
        // Check if path is excluded from authentication
        if (isExcluded(path)) {
            chain.doFilter(request, response);
            return;
        }
        
        // Check for valid session
        HttpSession session = httpRequest.getSession(false);
        if (session == null || session.getAttribute("authenticated") == null) {
            
            // Check for "Remember Me" cookie
            String username = checkRememberMeCookie(httpRequest);
            if (username != null) {
                // Auto-login with remember me
                session = httpRequest.getSession(true);
                session.setAttribute("username", username);
                session.setAttribute("authenticated", true);
                session.setAttribute("loginMethod", "remember_me");
            } else {
                // Redirect to login page
                String loginURL = contextPath + "/login?redirect=" + 
                                URLEncoder.encode(requestURI, "UTF-8");
                httpResponse.sendRedirect(loginURL);
                return;
            }
        }
        
        // Check session timeout
        long lastAccessed = session.getLastAccessedTime();
        long sessionTimeout = session.getMaxInactiveInterval() * 1000; // Convert to milliseconds
        
        if (System.currentTimeMillis() - lastAccessed > sessionTimeout) {
            session.invalidate();
            String loginURL = contextPath + "/login?expired=true&redirect=" + 
                            URLEncoder.encode(requestURI, "UTF-8");
            httpResponse.sendRedirect(loginURL);
            return;
        }
        
        // Add user info to request for downstream processing
        String username = (String) session.getAttribute("username");
        request.setAttribute("currentUser", username);
        
        // Continue the filter chain
        chain.doFilter(request, response);
    }
    
    private boolean isExcluded(String path) {
        return excludedPaths.stream().anyMatch(path::startsWith);
    }
    
    private String checkRememberMeCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("rememberToken".equals(cookie.getName())) {
                    return validateRememberToken(cookie.getValue());
                }
            }
        }
        return null;
    }
    
    private String validateRememberToken(String token) {
        // Validate token against database
        // Return username if valid, null if invalid
        return null; // Implement actual validation
    }
    
    @Override
    public void destroy() {
        excludedPaths.clear();
    }
}

Authorization Filter

/**
 * Authorization filter that checks user roles and permissions
 */
@WebFilter(
    filterName = "AuthorizationFilter",
    urlPatterns = {"/admin/*"},
    dispatcherTypes = {DispatcherType.REQUEST}
)
public class AuthorizationFilter implements Filter {
    
    private Map<String, Set<String>> pathRoleMapping;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Initialize path-to-role mappings
        pathRoleMapping = new HashMap<>();
        
        // Admin paths require admin role
        pathRoleMapping.put("/admin/users", Set.of("admin", "user_manager"));
        pathRoleMapping.put("/admin/system", Set.of("admin"));
        pathRoleMapping.put("/admin/reports", Set.of("admin", "manager"));
        pathRoleMapping.put("/admin/settings", Set.of("admin"));
        
        // Load additional mappings from init parameters
        loadRoleMappingsFromConfig(filterConfig);
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        String requestURI = httpRequest.getRequestURI();
        String contextPath = httpRequest.getContextPath();
        String path = requestURI.substring(contextPath.length());
        
        // Get required roles for this path
        Set<String> requiredRoles = getRequiredRoles(path);
        
        if (requiredRoles.isEmpty()) {
            // No specific roles required
            chain.doFilter(request, response);
            return;
        }
        
        // Get user roles from session or database
        Set<String> userRoles = getUserRoles(httpRequest);
        
        // Check if user has any of the required roles
        boolean hasPermission = userRoles.stream()
                                        .anyMatch(requiredRoles::contains);
        
        if (!hasPermission) {
            // Log unauthorized access attempt
            String username = (String) httpRequest.getSession().getAttribute("username");
            System.out.println("Unauthorized access attempt by user '" + username + 
                             "' to path '" + path + "'. Required roles: " + requiredRoles + 
                             ", User roles: " + userRoles);
            
            // Send forbidden response
            httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, 
                                 "Access denied. Insufficient privileges.");
            return;
        }
        
        // User has permission, continue
        chain.doFilter(request, response);
    }
    
    private Set<String> getRequiredRoles(String path) {
        // Find the most specific matching path
        return pathRoleMapping.entrySet().stream()
                             .filter(entry -> path.startsWith(entry.getKey()))
                             .max(Comparator.comparing(entry -> entry.getKey().length()))
                             .map(Map.Entry::getValue)
                             .orElse(Collections.emptySet());
    }
    
    private Set<String> getUserRoles(HttpServletRequest request) {
        String username = (String) request.getSession().getAttribute("username");
        if (username != null) {
            // In a real application, load roles from database
            return loadUserRolesFromDatabase(username);
        }
        return Collections.emptySet();
    }
    
    private Set<String> loadUserRolesFromDatabase(String username) {
        // Mock implementation - replace with actual database lookup
        if ("admin".equals(username)) {
            return Set.of("admin", "manager", "user");
        } else if ("manager".equals(username)) {
            return Set.of("manager", "user");
        } else {
            return Set.of("user");
        }
    }
    
    private void loadRoleMappingsFromConfig(FilterConfig filterConfig) {
        // Load additional role mappings from configuration
        Enumeration<String> paramNames = filterConfig.getInitParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            if (paramName.startsWith("path.")) {
                String path = paramName.substring(5); // Remove "path." prefix
                String rolesStr = filterConfig.getInitParameter(paramName);
                Set<String> roles = Arrays.stream(rolesStr.split(","))
                                         .map(String::trim)
                                         .collect(Collectors.toSet());
                pathRoleMapping.put("/" + path, roles);
            }
        }
    }
    
    @Override
    public void destroy() {
        pathRoleMapping.clear();
    }
}

Security Headers Filter

/**
 * Security filter that adds protective HTTP headers
 */
@WebFilter(
    filterName = "SecurityHeadersFilter",
    urlPatterns = {"/*"},
    dispatcherTypes = {DispatcherType.REQUEST}
)
public class SecurityHeadersFilter implements Filter {
    
    private boolean enableHSTS;
    private boolean enableCSP;
    private String cspPolicy;
    private boolean enableXFrameOptions;
    private String frameOptionsValue;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Configure security headers from init parameters
        enableHSTS = Boolean.parseBoolean(
            filterConfig.getInitParameter("enableHSTS"));
        
        enableCSP = Boolean.parseBoolean(
            filterConfig.getInitParameter("enableCSP"));
        cspPolicy = filterConfig.getInitParameter("cspPolicy");
        if (cspPolicy == null) {
            cspPolicy = "default-src 'self'; script-src 'self' 'unsafe-inline'; " +
                       "style-src 'self' 'unsafe-inline'";
        }
        
        enableXFrameOptions = Boolean.parseBoolean(
            filterConfig.getInitParameter("enableXFrameOptions"));
        frameOptionsValue = filterConfig.getInitParameter("frameOptionsValue");
        if (frameOptionsValue == null) {
            frameOptionsValue = "DENY";
        }
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // Add security headers
        addSecurityHeaders(httpRequest, httpResponse);
        
        // Continue the filter chain
        chain.doFilter(request, response);
    }
    
    private void addSecurityHeaders(HttpServletRequest request, 
                                   HttpServletResponse response) {
        
        // X-Content-Type-Options: Prevent MIME type sniffing
        response.setHeader("X-Content-Type-Options", "nosniff");
        
        // X-XSS-Protection: Enable XSS filtering
        response.setHeader("X-XSS-Protection", "1; mode=block");
        
        // X-Frame-Options: Prevent clickjacking
        if (enableXFrameOptions) {
            response.setHeader("X-Frame-Options", frameOptionsValue);
        }
        
        // Content Security Policy: Prevent XSS and data injection
        if (enableCSP) {
            response.setHeader("Content-Security-Policy", cspPolicy);
        }
        
        // Strict-Transport-Security: Enforce HTTPS
        if (enableHSTS && request.isSecure()) {
            response.setHeader("Strict-Transport-Security", 
                             "max-age=31536000; includeSubDomains");
        }
        
        // Referrer-Policy: Control referrer information
        response.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
        
        // Permissions-Policy: Control browser features
        response.setHeader("Permissions-Policy", 
                         "camera=(), microphone=(), geolocation=()");
    }
    
    @Override
    public void destroy() {
        // No cleanup needed
    }
}

CORS (Cross-Origin Resource Sharing) Filter

/**
 * CORS filter for handling cross-origin requests
 */
@WebFilter(
    filterName = "CORSFilter",
    urlPatterns = {"/api/*"},
    dispatcherTypes = {DispatcherType.REQUEST}
)
public class CORSFilter implements Filter {
    
    private Set<String> allowedOrigins;
    private Set<String> allowedMethods;
    private Set<String> allowedHeaders;
    private boolean allowCredentials;
    private int maxAge;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Configure CORS settings
        String originsParam = filterConfig.getInitParameter("allowedOrigins");
        if (originsParam != null) {
            allowedOrigins = Arrays.stream(originsParam.split(","))
                                  .map(String::trim)
                                  .collect(Collectors.toSet());
        } else {
            allowedOrigins = Set.of("*");
        }
        
        String methodsParam = filterConfig.getInitParameter("allowedMethods");
        if (methodsParam != null) {
            allowedMethods = Arrays.stream(methodsParam.split(","))
                                  .map(String::trim)
                                  .collect(Collectors.toSet());
        } else {
            allowedMethods = Set.of("GET", "POST", "PUT", "DELETE", "OPTIONS");
        }
        
        String headersParam = filterConfig.getInitParameter("allowedHeaders");
        if (headersParam != null) {
            allowedHeaders = Arrays.stream(headersParam.split(","))
                                  .map(String::trim)
                                  .collect(Collectors.toSet());
        } else {
            allowedHeaders = Set.of("Origin", "Content-Type", "Accept", 
                                   "Authorization", "X-Requested-With");
        }
        
        allowCredentials = Boolean.parseBoolean(
            filterConfig.getInitParameter("allowCredentials"));
        
        String maxAgeParam = filterConfig.getInitParameter("maxAge");
        maxAge = maxAgeParam != null ? Integer.parseInt(maxAgeParam) : 3600;
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        String origin = httpRequest.getHeader("Origin");
        String method = httpRequest.getMethod();
        
        // Check if origin is allowed
        if (origin != null && (allowedOrigins.contains("*") || allowedOrigins.contains(origin))) {
            
            // Set CORS headers
            httpResponse.setHeader("Access-Control-Allow-Origin", 
                                 allowedOrigins.contains("*") ? "*" : origin);
            
            if (allowCredentials && !allowedOrigins.contains("*")) {
                httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            }
            
            // Handle preflight request
            if ("OPTIONS".equals(method)) {
                String requestMethod = httpRequest.getHeader("Access-Control-Request-Method");
                String requestHeaders = httpRequest.getHeader("Access-Control-Request-Headers");
                
                if (requestMethod != null && allowedMethods.contains(requestMethod)) {
                    httpResponse.setHeader("Access-Control-Allow-Methods", 
                                         String.join(", ", allowedMethods));
                }
                
                if (requestHeaders != null) {
                    Set<String> requestedHeaders = Arrays.stream(requestHeaders.split(","))
                                                        .map(String::trim)
                                                        .collect(Collectors.toSet());
                    
                    if (allowedHeaders.containsAll(requestedHeaders)) {
                        httpResponse.setHeader("Access-Control-Allow-Headers", 
                                             String.join(", ", allowedHeaders));
                    }
                }
                
                httpResponse.setHeader("Access-Control-Max-Age", String.valueOf(maxAge));
                httpResponse.setStatus(HttpServletResponse.SC_OK);
                return; // Don't continue the chain for preflight requests
            }
        }
        
        // Continue the filter chain
        chain.doFilter(request, response);
    }
    
    @Override
    public void destroy() {
        allowedOrigins.clear();
        allowedMethods.clear();
        allowedHeaders.clear();
    }
}

Secure Servlet Example

/**
 * Example servlet with comprehensive security annotations
 */
@WebServlet("/secure/admin")
@ServletSecurity(
    value = @HttpConstraint(
        rolesAllowed = {"admin"},
        transportGuarantee = TransportGuarantee.CONFIDENTIAL
    ),
    httpMethodConstraints = {
        @HttpMethodConstraint(
            value = "GET",
            rolesAllowed = {"admin", "manager"}
        ),
        @HttpMethodConstraint(
            value = "POST",
            rolesAllowed = {"admin"}
        ),
        @HttpMethodConstraint(
            value = "DELETE",
            rolesAllowed = {"admin"},
            transportGuarantee = TransportGuarantee.CONFIDENTIAL
        )
    }
)
public class SecureAdminServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        // Verify user authentication
        Principal userPrincipal = request.getUserPrincipal();
        if (userPrincipal == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        
        // Check specific role
        if (!request.isUserInRole("admin") && !request.isUserInRole("manager")) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter out = response.getWriter();
        
        out.println("{");
        out.println("  \"message\": \"Admin panel access granted\",");
        out.println("  \"user\": \"" + userPrincipal.getName() + "\",");
        out.println("  \"isAdmin\": " + request.isUserInRole("admin") + ",");
        out.println("  \"isManager\": " + request.isUserInRole("manager") + ",");
        out.println("  \"authType\": \"" + request.getAuthType() + "\"");
        out.println("}");
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        // Only admin users can perform POST operations
        if (!request.isUserInRole("admin")) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        
        // Process admin operation
        String operation = request.getParameter("operation");
        
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("{\"status\":\"success\",\"operation\":\"" + operation + "\"}");
    }
}

This comprehensive coverage of security and filtering provides all the tools needed for implementing robust security measures, access control, and request/response processing in servlet applications.

Install with Tessl CLI

npx tessl i tessl/maven-javax-servlet--javax-servlet-api

docs

async-processing.md

http-processing.md

index.md

listeners-events.md

security-filtering.md

servlet-lifecycle.md

session-cookies.md

tile.json