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

  1. Configure Spring Security in the application context (e.g. @EnableWebSecurity with a SecurityFilterChain) so the default WebSecurityExpressionHandler/SecurityExpressionHandler<FilterInvocation> bean is published.
  2. 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).
  3. Verify spring-security-web, spring-security-config, and spring-security-taglibs versions are aligned on the classpath.
  4. 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

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


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)