Jenkins Security Authentication Flow: How HudsonFilter and HttpSessionContextIntegrationFilter2 Work Together
Jenkins authenticates users through a two-filter architecture where HudsonFilter acts as the entry point for request processing and Stapler routing, while HttpSessionContextIntegrationFilter2 persists the Authentication object in the HTTP session to maintain security context across stateless requests.
Jenkins operates within a standard servlet container (Jetty, Tomcat, etc.) and delegates request-level security processing to a specialized filter chain. The Jenkins security authentication flow relies on two pivotal classes in the hudson.security package—HudsonFilter and HttpSessionContextIntegrationFilter2—to bridge the servlet container's HTTP handling with Jenkins' pluggable SecurityRealm architecture.
HudsonFilter: The Request Entry Point
HudsonFilter serves as the primary servlet filter that intercepts every incoming request to Jenkins. Located in core/src/main/java/hudson/security/HudsonFilter.java, this filter initializes the Stapler request-routing engine and orchestrates the security filter chain.
The filter executes the following sequence in its doFilter method:
- Wraps the raw request in a
StaplerRequestto enable Jenkins' URL-to-method mapping - Invokes
ChainedServletFilter2to sequentially execute security filters includingBasicAuthenticationFilter,AuthenticationProcessingFilter2, andCrumbFilter - Establishes the
SecurityContextby binding any validAuthenticationobject to the thread viaSecurityContextHolder - Dispatches to Stapler for final handling by the appropriate Jenkins action or REST endpoint
// Simplified flow inside HudsonFilter#doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
// 1. Wrap request for Stapler
StaplerRequest staplerRequest = new StaplerRequestWrapper((HttpServletRequest) req);
// 2. Run the Jenkins-specific filter chain
chainedFilter.doFilter(staplerRequest, res, chain);
// 3. Security context is now available for the rest of the request
// (e.g., Jenkins.getAuthentication())
}
HttpSessionContextIntegrationFilter2: Session Security Bridge
While HudsonFilter handles request entry, HttpSessionContextIntegrationFilter2 manages the persistence of authentication state across multiple HTTP requests. This filter, found in core/src/main/java/hudson/security/HttpSessionContextIntegrationFilter2.java, bridges the servlet container's HTTP session with Jenkins' security framework.
The filter performs three critical operations:
- Retrieval: On each request, it checks the HTTP session for an existing
SecurityContextstored under the attribute keyHttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY - Storage: After successful authentication (performed by upstream filters like
BasicAuthenticationFilter), it persists theAuthenticationobject in the session - Thread binding: It pushes the retrieved or created
SecurityContextontoSecurityContextHolderso thatJenkins.getAuthentication()returns the current user throughout the request lifecycle
// Core logic from HttpSessionContextIntegrationFilter2#doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) req;
HttpSession session = httpReq.getSession(false);
// 1. Retrieve stored SecurityContext (if any)
SecurityContext ctx = (session == null) ? null
: (SecurityContext) session.getAttribute(
HttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY);
// 2. If request carries a new Authentication (post-login), store it
if (SecurityContextHolder.getContext().getAuthentication() != null) {
ctx = SecurityContextHolder.getContext();
if (session != null) {
session.setAttribute(
HttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY, ctx);
}
}
// 3. Bind context to thread for downstream processing
try {
SecurityContextHolder.setContext(ctx != null ? ctx :
SecurityContextHolder.createEmptyContext());
chain.doFilter(req, res);
} finally {
SecurityContextHolder.clearContext();
}
}
End-to-End Authentication Sequence
Understanding how these filters interact reveals the complete Jenkins security authentication flow:
| Phase | Component | Action |
|---|---|---|
| Initial Request | HudsonFilter → BasicAuthenticationFilter |
Extracts credentials (HTTP Basic, form data, or API token) and validates against the configured SecurityRealm (LDAP, Jenkins database, OAuth) |
| Authentication | AuthenticationProcessingFilter2 |
Creates an Authentication object upon successful credential validation |
| Session Persistence | HttpSessionContextIntegrationFilter2 |
Stores the Authentication in the HTTP session attribute SECURITY_CONTEXT_KEY |
| Subsequent Requests | HudsonFilter → HttpSessionContextIntegrationFilter2 |
Retrieves the stored Authentication from the session and binds it to SecurityContextHolder |
| Authorization | AuthorizationStrategy |
Validates the current Authentication against permissions before allowing access to protected resources |
The filter order declared in Jenkins' generated web.xml ensures that HttpSessionContextIntegrationFilter2 executes after authentication filters create the Authentication object but before any business logic requires the security context.
Architectural Separation: Why Two Filters?
Jenkins separates these concerns to support both stateless API access and stateful web sessions:
-
HudsonFilterprovides a generic entry point that guarantees every request undergoes Stapler processing and security chain evaluation. It remains agnostic to session management, making it suitable for API calls that authenticate on every request without cookies. -
HttpSessionContextIntegrationFilter2isolates session-binding logic, allowing multiple authentication mechanisms (form login, basic auth, token-based) to reuse the same session persistence code without duplication.
This separation enables API clients to remain stateless while providing browser users with persistent login sessions through standard HTTP cookies.
Practical Code Examples
Registering a Custom SecurityRealm
// In a Jenkins plugin's initialization code
@Extension
public class MyRealmPlugin extends Plugin {
@Override
public void start() throws Exception {
Jenkins.get().setSecurityRealm(new MyCustomSecurityRealm());
// Configure authorization strategy
Jenkins.get().setAuthorizationStrategy(
new GlobalMatrixAuthorizationStrategy());
}
}
Accessing the Current Authentication
public class MyAction implements RootAction {
@Override
public String getDisplayName() {
// Obtains user from SecurityContext populated by the filter chain
Authentication auth = Jenkins.getAuthentication();
User user = User.getById(auth.getName(), false);
return "Hello, " + (user != null ? user.getFullName() : "anonymous");
}
}
Implementing Secure Logout
public void doLogout(StaplerRequest req, StaplerResponse rsp)
throws IOException {
HttpSession session = req.getSession(false);
if (session != null) {
session.removeAttribute(
HttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY);
}
SecurityContextHolder.clearContext(); // Clear thread-local context
rsp.sendRedirect2(req.getContextPath() + "/login");
}
Summary
HudsonFilterinhudson/security/HudsonFilter.javaacts as the mandatory entry point for all Jenkins requests, initializing Stapler and executing the security filter chain.HttpSessionContextIntegrationFilter2inhudson/security/HttpSessionContextIntegrationFilter2.javamaintains authentication state by storing and retrievingSecurityContextobjects from the HTTP session.- The Jenkins security authentication flow separates request processing from session management to support both stateless API clients and stateful browser sessions.
- Filter ordering in the servlet container ensures that
HttpSessionContextIntegrationFilter2executes after authentication filters create credentials but before authorization checks occur. - Developers can access the current authentication via
Jenkins.getAuthentication(), which returns theAuthenticationobject bound to the thread by these filters.
Frequently Asked Questions
How does HttpSessionContextIntegrationFilter2 handle concurrent requests from the same user?
Each HTTP request executes in its own thread, and HttpSessionContextIntegrationFilter2 retrieves the SecurityContext from the shared HTTP session at the start of each request. The filter binds this context to the thread-local SecurityContextHolder, ensuring thread isolation while allowing multiple simultaneous requests to share the same authentication credentials safely.
Can Jenkins operate without HttpSessionContextIntegrationFilter2?
While technically possible for purely stateless API access, removing this filter would force users to re-authenticate on every single request, eliminating the ability to maintain login sessions via browser cookies. The filter is essential for the standard web UI experience where users expect to remain logged in across multiple page loads.
Where does HudsonFilter fit in the standard servlet filter chain?
HudsonFilter is registered in the servlet container's web.xml (generated at runtime) and serves as the primary dispatch filter for all Jenkins URLs. It executes after container-level filters but before individual Jenkins components process the request, ensuring that SecurityContext is properly established regardless of whether the request targets a UI page, REST endpoint, or plugin resource.
What happens to the security context when a session expires?
When the HTTP session expires or is invalidated, HttpSessionContextIntegrationFilter2 cannot retrieve a stored SecurityContext on subsequent requests. The filter creates an empty context, causing Jenkins.getAuthentication() to return an anonymous or null authentication. The user must then re-authenticate through the configured SecurityRealm to establish a new valid session.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →