Migrate from deprecated Spring security AccessDecisionVoter

157 Views Asked by At

We are trying to migrate away from spring-security's deprecated AccessDecisionVoter

Our Config includes a decisionManager that takes a few AccessDecisionVoter and then decides based on all the input.

class CollectingUnanimousBasedAccessDecisionManager extends AbstractAccessDecisionManager {

  CollectingUnanimousBasedAccessDecisionManager(List<AccessRightDecisionVoter> decisionVoters) {
    super(decisionVoters)
  }

  void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
      throws AccessDeniedException {
    int deniedDecisions = 0

    List violatedAccessRights = []

    for (AccessDecisionVoter voter : getDecisionVoters()) {
      int result = voter.vote(authentication, object, configAttributes)

      switch (result) {
        case AccessDecisionVoter.ACCESS_DENIED:
          deniedDecisions++
          violatedAccessRights.addAll(((AccessRightDecisionVoter) voter).getSupportedAccessRights())
          break
      }
    }

    if (deniedDecisions > 0) {
      throw new AccessDeniedExceptionWithCause("Access is denied", violatedAccessRights)
    }
  }
}

Our Config looks like this:


@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
class AuthorizationConfig extends GlobalMethodSecurityConfiguration {

  @Autowired
  ServiceCallVoter serviceCallVoter
  @Autowired
  AuthorityAndScopeVoter authorityAndScopeVoter
  @Autowired
  CallOtherVoterVoter callOtherVoterVoter
  @Autowired
  AuthorityVoter authorityVoter

  @Autowired
  Environment environment

  @Override
  AccessDecisionManager accessDecisionManager() {
    new CollectingUnanimousBasedAccessDecisionManager([
        serviceCallVoter,
        authorityAndScopeVoter,
        callOtherVoterVoter,
        authorityVoter
    ])
  }
}

We then have for example a ServiceCallVoter, which has other dependencies and calls eg. a database for input, or another service:

@Component
class ServiceCallVoter extends AbstractIdVoter {

  @Override
  String[] getSupportedAccessRights() {
    [SERVICE_CALL]
  }

  @Override
  int voteInternal(String id, Authentication authentication, ReflectiveMethodInvocation methodToAuthorize, Collection<ConfigAttribute> configAttributes) {
    if (true) { // some service call
      return ACCESS_GRANTED
    }
    return ACCESS_DENIED
  }
}

And also for example an Authority Voter, which is looking into the Authentication authorities or scopes:

@Component
abstract class AbstractAuthorityVoter extends AccessRightDecisionVoter {

  abstract List<String> getAuthorities()

  @Override
  int vote(Authentication authentication, ReflectiveMethodInvocation objectToAuthorize, Collection<ConfigAttribute> configAttributes) {
    if (!supportsAnyConfigAttribute(configAttributes)) {
      return ACCESS_ABSTAIN
    }

    def hasAuthority = authentication.authorities.any {
      String authority = it.authority
      getAuthorities().any { it == authority }
    }
    return hasAuthority ? ACCESS_GRANTED : ACCESS_DENIED
  }
}

For the simpler services only using the AbstractAuthorityVoter we found the solution to use a @PreAuthorize annotation:

  @PreAuthorize("hasAnyAuthority('AUTHORITY_1', 'SCOPE_SCHREIBEN')")

But for the more complex services, which use a combination of multiple voters, (@Secured([SCOPE_SCHREIBEN, CALL_OTHER_VOTER])), we could not yet get it to work.

How would you migrate those classes to the AuthorizationManager?

All the classes can be found https://github.com/huehnerlady/demo-spring-security-6

0

There are 0 best solutions below