Mechanics of Apache Shiro BasicHttpAuthenticationFilter
Request Handling Workflow
The core logic of the filter is orchestrated within the onPreHandle method. This method determines the fate of a request by evaluating two distinct conditions: isAccessAllowed and onAccessDenied. These are combined using logical OR short-circuiting. The system first checks if access is permitted; only if this check fails does it proceed to handle the denial.
Access Permission Check
The isAccessAllowed method is responsible for verifying the authentication status of the current user. It retrieves the Subject instance associated with the request and confirms whether the user has already authenticated successfully.
protected boolean checkUserAccess(ServletRequest req, ServletResponse resp, Object mappedValue) {
Subject currentUser = getSubject(req, resp);
return currentUser.isAuthenticated();
}If the user is not authenticated, the filter evaluates whether the request matches a login URL or falls under a permissive (anonymous) path configuration. If neither condition is met, the request is treated as unauthorized.
Handling Access Denial
When access is not allowed, the onAccessDenied method is invoked. This method inspects the request headers to determine if the client is attempting to authenticate via isLoginAttempt.
protected boolean handleAccessDenial(ServletRequest req, ServletResponse resp) throws Exception {
boolean authenticated = false;
if (isLoginAttempt(req, resp)) {
authenticated = executeLogin(req, resp);
}
if (!authenticated) {
sendChallenge(resp);
}
return authenticated;
}If credentials are present in the header, the filter attempts to log the user in. If no credentials are detected or the login fails, the filter sends an HTTP 401 challenge response to the client, prompting for credentials.
Login Execution and Token Creation
BasicHttpAuthenticationFilter relies on the parent AuthenticatingFilter to perform the actual login via executeLogin. This process involves creating an authentication token and delegating verification to the SecurityManager.
protected boolean performLogin(ServletRequest req, ServletResponse resp) throws Exception {
AuthenticationToken authcToken = createToken(req, resp);
if (authcToken == null) {
throw new IllegalStateException("AuthenticationToken cannot be null.");
}
try {
Subject user = getSubject(req, resp);
user.login(authcToken);
return onLoginSuccess(authcToken, user, req, resp);
} catch (AuthenticationException e) {
return onLoginFailure(authcToken, e, req, resp);
}
}The createToken method parses the HTTP Authorization header to extract credentials. If the header is missing or malformed, an empty token is generated.
protected AuthenticationToken generateToken(ServletRequest req, ServletResponse resp) {
String authzHeader = getAuthzHeader(req);
if (StringUtils.isEmpty(authzHeader)) {
return createToken("", "", req, resp);
}
String[] credentials = getPrincipalsAndCredentials(authzHeader, req);
if (credentials == null || credentials.length < 2) {
String username = (credentials == null || credentials.length == 0) ? "" : credentials[0];
return createToken(username, "", req, resp);
}
return createToken(credentials[0], credentials[1], req, resp);
}Upon successful token creation, the Subject.login method triggers the internal security pipeline. The SecurityManager delegates the verification to the configured Realm, completing the authentication cycle if no exceptions are thrown.