Java Servlet API specification defining core interfaces and classes for web application development
—
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.
/**
* 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();
}/**
* 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
}
}/**
* 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);
}
}/**
* 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 {};
}/**
* 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);
}
}/**
* 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
}/**
* 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;
}/**
* 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 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 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 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();
}
}/**
* 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