As we'll see in the Technical Overview chapter, Spring
Security provides interceptors which control access to secure objects such as method invocations
or web requests. A pre-invocation decision on whether the invocation is allowed to proceed is made by
the AccessDecisionManager
.
The AccessDecisionManager
is called by the
AbstractSecurityInterceptor
and is responsible for
making final access control decisions. The
AccessDecisionManager
interface contains three
methods:
void decide(Authentication authentication, Object secureObject, ConfigAttributeDefinition config) throws AccessDeniedException; boolean supports(ConfigAttribute attribute); boolean supports(Class clazz);
As can be seen from the first method, the
AccessDecisionManager
is passed via method
parameters all information that is likely to be of value in assessing
an authorization decision. In particular, passing the secure
Object
enables those arguments contained in the
actual secure object invocation to be inspected. For example, let's
assume the secure object was a MethodInvocation
. It
would be easy to query the MethodInvocation
for any
Customer
argument, and then implement some sort of
security logic in the AccessDecisionManager
to
ensure the principal is permitted to operate on that customer.
Implementations are expected to throw an
AccessDeniedException
if access is denied.
The supports(ConfigAttribute)
method is
called by the AbstractSecurityInterceptor
at
startup time to determine if the
AccessDecisionManager
can process the passed
ConfigAttribute
. The
supports(Class)
method is called by a security
interceptor implementation to ensure the configured
AccessDecisionManager
supports the type of secure
object that the security interceptor will present.
Whilst users can implement their own AccessDecisionManager
to control all aspects of
authorization, Spring Security includes several AccessDecisionManager
implementations that are
based on voting. Figure 22.1, “Voting Decision Manager” illustrates the relevant classes.
Using this approach, a series of
AccessDecisionVoter
implementations are polled on
an authorization decision. The
AccessDecisionManager
then decides whether or not
to throw an AccessDeniedException
based on its
assessment of the votes.
The AccessDecisionVoter
interface has three
methods:
int vote(Authentication authentication, Object object, ConfigAttributeDefinition config); boolean supports(ConfigAttribute attribute); boolean supports(Class clazz);
Concrete implementations return an int
, with
possible values being reflected in the
AccessDecisionVoter
static fields
ACCESS_ABSTAIN
, ACCESS_DENIED
and ACCESS_GRANTED
. A voting implementation will
return ACCESS_ABSTAIN
if it has no opinion on an
authorization decision. If it does have an opinion, it must return
either ACCESS_DENIED
or
ACCESS_GRANTED
.
There are three concrete
AccessDecisionManager
s provided with Spring
Security that tally the votes. The ConsensusBased
implementation will grant or deny access based on the consensus of
non-abstain votes. Properties are provided to control behavior in the
event of an equality of votes or if all votes are abstain. The
AffirmativeBased
implementation will grant access
if one or more ACCESS_GRANTED
votes were received
(i.e. a deny vote will be ignored, provided there was at least one grant
vote). Like the ConsensusBased
implementation,
there is a parameter that controls the behavior if all voters abstain.
The UnanimousBased
provider expects unanimous
ACCESS_GRANTED
votes in order to grant access,
ignoring abstains. It will deny access if there is any
ACCESS_DENIED
vote. Like the other implementations,
there is a parameter that controls the behaviour if all voters
abstain.
It is possible to implement a custom
AccessDecisionManager
that tallies votes
differently. For example, votes from a particular
AccessDecisionVoter
might receive additional
weighting, whilst a deny vote from a particular voter may have a veto
effect.
The most commonly used AccessDecisionVoter
provided with Spring Security is the simple RoleVoter
, which treats
configuration attributes as simple role names and votes to grant access if the user has been assigned
that role.
It will vote if any ConfigAttribute begins with the prefix ROLE_
.
It will vote to grant access if there is a GrantedAuthority
which returns a
String
representation (via the
getAuthority()
method) exactly equal to one or more
ConfigAttributes
starting with
ROLE_
. If there is no exact match of any
ConfigAttribute
starting with
ROLE_
, the RoleVoter
will vote
to deny access. If no ConfigAttribute
begins with
ROLE_
, the voter will abstain.
RoleVoter
is case sensitive on comparisons as well
as the ROLE_
prefix.
It is also possible to implement a custom
AccessDecisionVoter
. Several examples are provided
in Spring Security unit tests, including
ContactSecurityVoter
and
DenyVoter
. The
ContactSecurityVoter
abstains from voting decisions
where a CONTACT_OWNED_BY_CURRENT_USER
ConfigAttribute
is not found. If voting, it queries
the MethodInvocation
to extract the owner of the
Contact
object that is subject of the method call.
It votes to grant access if the Contact
owner
matches the principal presented in the
Authentication
object. It could have just as easily
compared the Contact
owner with some
GrantedAuthority
the
Authentication
object presented. All of this is
achieved with relatively few lines of code and demonstrates the
flexibility of the authorization model.