spring-projects/spring-security · error · AccessDeniedException

Access is denied

Error message

Access is denied

What it means

ConsensusBased.decide() throws AccessDeniedException when deny votes outnumber grant votes (deny > grant). The consensus strategy totals ACCESS_GRANTED vs ACCESS_DENIED votes and denies when the majority denies.

Source

Thrown at access/src/main/java/org/springframework/security/access/vote/ConsensusBased.java:84

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException {
		int grant = 0;
		int deny = 0;
		for (AccessDecisionVoter voter : getDecisionVoters()) {
			int result = voter.vote(authentication, object, configAttributes);
			switch (result) {
				case AccessDecisionVoter.ACCESS_GRANTED -> grant++;
				case AccessDecisionVoter.ACCESS_DENIED -> deny++;
				default -> {
				}
			}
		}
		if (grant > deny) {
			return;
		}
		if (deny > grant) {
			throw new AccessDeniedException(
					this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
		}
		if ((grant == deny) && (grant != 0)) {
			if (this.allowIfEqualGrantedDeniedDecisions) {
				return;
			}
			throw new AccessDeniedException(
					this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
		}
		// To get this far, every AccessDecisionVoter abstained
		checkAllowIfAllAbstainDecisions();
	}

	public boolean isAllowIfEqualGrantedDeniedDecisions() {
		return this.allowIfEqualGrantedDeniedDecisions;
	}

	public void setAllowIfEqualGrantedDeniedDecisions(boolean allowIfEqualGrantedDeniedDecisions) {

View on GitHub (pinned to 96852e8860)

Solutions

  1. Grant the missing authorities so grants >= denies
  2. Adjust which voters are registered or the attributes they evaluate
  3. Set allowIfEqualGrantedDeniedDecisions(true) only for tie cases (not this case, but review flag)
  4. Audit voter list for unintended duplicate or overly strict voters

Example fix

// before
user authorities: [ROLE_USER]; access="ROLE_USER,ROLE_ADMIN" -> 1 deny, 1 grant

// after
user authorities: [ROLE_USER, ROLE_ADMIN] -> 2 grants, 0 denies
Defensive patterns

Strategy: validation

Validate before calling

long grants = voters.stream().filter(v -> v.vote(auth, obj, attrs) == AccessDecisionVoter.ACCESS_GRANTED).count();
long denies = voters.stream().filter(v -> v.vote(auth, obj, attrs) == AccessDecisionVoter.ACCESS_DENIED).count();
if (denies > grants) throw new AccessDeniedException("Would be denied by consensus");

Type guard

null

Try / catch

try {
    consensusBased.decide(auth, object, attrs);
} catch (AccessDeniedException e) {
    throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Consensus denied");
}

Prevention

When it happens

Trigger: An invocation where more voters voted ACCESS_DENIED than ACCESS_GRANTED, e.g. one affirmative, one deny, one abstain with allowIfEqualGrantedDeniedDecisions irrelevant or unequal case.

Common situations: Users partially matching role requirements across multiple voters; symmetric configurations where one more role is missing than present; duplicated voters amplifying denies.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/de3e20a2ec77fb46. Report an issue: GitHub.