Authorization ArchitectureAuthoritiesAs we saw in the technical overview, all
Authentication implementations store a list of
GrantedAuthority objects. These represent the authorities
that have been granted to the principal. The GrantedAuthority
objects are inserted into the Authentication object by the
AuthenticationManager and are later read by
AccessDecisionManagers when making authorization
decisions.GrantedAuthority is an interface with only one method:
String getAuthority();
This method allows
AccessDecisionManagers to obtain a precise
String representation of the
GrantedAuthority. By returning a representation as a
String, a GrantedAuthority can be easily
read by most AccessDecisionManagers. If a
GrantedAuthority cannot be precisely represented as a
String, the GrantedAuthority is considered
complex and getAuthority() must return
null.An example of a complexGrantedAuthority would be an implementation that stores a list
of operations and authority thresholds that apply to different customer account numbers.
Representing this complex GrantedAuthority as a
String would be quite difficult, and as a result the
getAuthority() method should return null. This will
indicate to any AccessDecisionManager that it will need to
specifically support the GrantedAuthority implementation in
order to understand its contents.Spring Security includes one concrete GrantedAuthority
implementation, GrantedAuthorityImpl. This allows any user-specified
String to be converted into a
GrantedAuthority. All
AuthenticationProviders included with the security architecture use
GrantedAuthorityImpl to populate the
Authentication object.Pre-Invocation Handling As we've also seen 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 AccessDecisionManagerThe 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,
List<ConfigAttribute> config) throws AccessDeniedException;
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
The AccessDecisionManager's decide
method is passed all the relevant information it needs in order to make 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.Voting-Based AccessDecisionManager ImplementationsWhilst users can implement their own
AccessDecisionManager to control all aspects of
authorization, Spring Security includes several
AccessDecisionManager implementations that are based on
voting. illustrates the relevant classes.Voting Decision ManagerUsing 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, List<ConfigAttribute> 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 AccessDecisionManagers 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.RoleVoter 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.AuthenticatedVoter Another voter which we've implicitly seen is the
AuthenticatedVoter, which can be used to differentiate between
anonymous, fully-authenticated and remember-me authenticated users. When we've used the
attribute IS_AUTHENTICATED_ANONYMOUSLY to grant anonymous access, this
attribute was being processed by the AuthenticatedVoter. See the
Javadoc for this class for more information. Custom VotersIt 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_USERConfigAttribute 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.After Invocation HandlingWhilst the AccessDecisionManager is called by the
AbstractSecurityInterceptor before proceeding with the secure object
invocation, some applications need a way of modifying the object actually returned by the
secure object invocation. Whilst you could easily implement your own AOP concern to achieve
this, Spring Security provides a convenient hook that has several concrete implementations
that integrate with its ACL capabilities. illustrates Spring Security's
AfterInvocationManager and its concrete implementations. After Invocation ImplementationLike many other parts of Spring Security, AfterInvocationManager has a
single concrete implementation, AfterInvocationProviderManager, which polls
a list of AfterInvocationProviders. Each
AfterInvocationProvider is allowed to modify the return object or throw
an AccessDeniedException. Indeed multiple providers can modify the object,
as the result of the previous provider is passed to the next in the list.Please be aware that if you're using AfterInvocationManager, you will
still need configuration attributes that allow the
MethodSecurityInterceptor's
AccessDecisionManager to allow an operation. If you're using
the typical Spring Security included AccessDecisionManager
implementations, having no configuration attributes defined for a particular secure method
invocation will cause each AccessDecisionVoter to abstain from
voting. In turn, if the AccessDecisionManager property
"allowIfAllAbstainDecisions" is false, an
AccessDeniedException will be thrown. You may avoid this potential issue
by either (i) setting "allowIfAllAbstainDecisions" to
true (although this is generally not recommended) or (ii) simply ensure
that there is at least one configuration attribute that an
AccessDecisionVoter will vote to grant access for. This
latter (recommended) approach is usually achieved through a ROLE_USER or
ROLE_AUTHENTICATED configuration attribute.