spring-projects/spring-security · error · IOException
No visible WebSecurityExpressionHandler instance could be…
Error message
No visible WebSecurityExpressionHandler instance could be found in the application context. There must be at least one in order to support expressions in JSP 'authorize' tags.
What it means
JSP 'authorize' tags (spring-security-taglibs) resolve a SecurityExpressionHandler for FilterInvocation from the ApplicationContext to evaluate access expressions. If the application context contains no such handler, the tag throws this IOException — expression-based authorize tags cannot work without a WebSecurity ExpressionHandler bean.
Solutions
- Configure Spring Security in the application context (e.g. @EnableWebSecurity with a SecurityFilterChain) so the default WebSecurityExpressionHandler/SecurityExpressionHandler<FilterInvocation> bean is published.
- Ensure the JSP/tag context can see the application context where Security is configured (correct ApplicationContext lookup — set the parent context or publish the handler in the same context).
- Verify spring-security-web, spring-security-config, and spring-security-taglibs versions are aligned on the classpath.
- As a fallback, use the url attribute form only after confirming WebInvocationPrivilegeEvaluator beans exist, or migrate authorization checks to controller/service layer with @PreAuthorize.
Example fix
// before
// no Spring Security configuration in the web context
// after
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(req -> req.anyRequest().authenticated());
return http.build();
}
} Defensive patterns
Strategy: try-catch
Validate before calling
Map<String, SecurityExpressionHandler> handlers = ctx.getBeansOfType(SecurityExpressionHandler.class);
if (handlers.values().stream().noneMatch(h -> FilterInvocation.class.equals(
GenericTypeResolver.resolveTypeArgument(h.getClass(), SecurityExpressionHandler.class)))) {
throw new IllegalStateException("No WebSecurityExpressionHandler in context; configure @EnableWebSecurity");
} Try / catch
try { ... tag evaluation ... } catch (IOException ex) { if (ex.getMessage().contains("WebSecurityExpressionHandler")) { log.error("Spring Security web config missing from this context"); } throw ex; } Prevention
- Configure @EnableWebSecurity / SecurityFilterChain so the expression handler bean exists
- Ensure the JSP's ApplicationContext is the Security-configured context (or its child)
- Keep spring-security-web/config/taglibs versions aligned
When it happens
Trigger: Using <sec:authorize access="..."> in a JSP when getApplicationContext().getBeansOfType(SecurityExpressionHandler.class) has no handler whose type argument resolves to FilterInvocation — typically because Spring Security's web support is not configured in this context at all.
Common situations: Spring Security jars on classpath but no @EnableWebSecurity / WebSecurityConfiguration in the application context; tag used in a context that cannot see the root context (wrong parent/child context setup in older Spring MVC); missing spring-security-config/web wiring; using the tag in a portlet or non-web context.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- No visible WebInvocationPrivilegeEvaluator instance could…
- A Bean named mvcHandlerMappingIntrospector of type…
- A Bean named mvcHandlerMappingIntrospector of type…
- A ReactiveSessionRegistry is needed for concurrent session…
- A ServerOneTimeTokenGenerationSuccessHandler is required to…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/d569b3d115c70221.
Report an issue: GitHub.
Appendix: source
Thrown at taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java:206
String[] names = appContext.getBeanNamesForType(SecurityContextHolderStrategy.class);
if (names.length == 1) {
SecurityContextHolderStrategy strategy = appContext.getBean(SecurityContextHolderStrategy.class);
return strategy.getContext();
}
return SecurityContextHolder.getContext();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private SecurityExpressionHandler<FilterInvocation> getExpressionHandler() throws IOException {
ApplicationContext appContext = getApplicationContext();
Map<String, SecurityExpressionHandler> handlers = appContext.getBeansOfType(SecurityExpressionHandler.class);
for (SecurityExpressionHandler handler : handlers.values()) {
if (FilterInvocation.class
.equals(GenericTypeResolver.resolveTypeArgument(handler.getClass(), SecurityExpressionHandler.class))) {
return handler;
}
}
throw new IOException("No visible WebSecurityExpressionHandler instance could be found in the application "
+ "context. There must be at least one in order to support expressions in JSP 'authorize' tags.");
}
private WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() throws IOException {
WebInvocationPrivilegeEvaluator privEvaluatorFromRequest = (WebInvocationPrivilegeEvaluator) getRequest()
.getAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE);
if (privEvaluatorFromRequest != null) {
return privEvaluatorFromRequest;
}
ApplicationContext ctx = getApplicationContext();
Map<String, WebInvocationPrivilegeEvaluator> wipes = ctx.getBeansOfType(WebInvocationPrivilegeEvaluator.class);
if (wipes.isEmpty()) {
throw new IOException(
"No visible WebInvocationPrivilegeEvaluator instance could be found in the application "
+ "context. There must be at least one in order to support the use of URL access checks in 'authorize' tags.");
}
return (WebInvocationPrivilegeEvaluator) wipes.values().toArray()[0];
}View on GitHub (pinned to 96852e8860)