As briefly mentioned in the Authentication section, all
Authentication
implementations are required to
store an array 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
AccessDecisionManager
s when making authorization
decisions.
GrantedAuthority
is an interface with only
one method:
String getAuthority();
This method allows AccessDecisionManager
s to
obtain a precise String
representation of the
GrantedAuthority
. By returning a representation as
a String
, a GrantedAuthority
can
be easily "read" by most AccessDecisionManager
s. If
a GrantedAuthority
cannot be precisely represented
as a String
, the
GrantedAuthority
is considered "complex" and
getAuthority()
must return
null
.
An example of a "complex" GrantedAuthority
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 complex, 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
AuthenticationProvider
s included with the security
architecture use GrantedAuthorityImpl
to populate
the Authentication
object.
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.
Whilst 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.
Figure 22.2, “After Invocation Implementation” illustrates Spring Security's
AfterInvocationManager
and its concrete
implementations.
Like many other parts of Spring Security,
AfterInvocationManager
has a single concrete
implementation, AfterInvocationProviderManager
,
which polls a list of AfterInvocationProvider
s.
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. Let's now consider our ACL-aware
implementations of AfterInvocationProvider
.
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
PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new
ACL module. The new ACL module is a significant rewrite of the
existing ACL module. The new module can be found under the
org.springframework.security.acls
package, with
the old ACL module under
org.springframework.security.acl
. We encourage
users to consider testing with the new ACL module and build
applications with it. The old ACL module should be considered
deprecated and may be removed from a future release. The following
information relates to the new ACL package, and is thus
recommended.
A common services layer method we've all written at one stage or another looks like this:
public Contact getById(Integer id);
Quite often, only principals with permission to read the
Contact
should be allowed to obtain it. In this
situation the AccessDecisionManager
approach
provided by the AbstractSecurityInterceptor
will
not suffice. This is because the identity of the
Contact
is all that is available before the
secure object is invoked. The
AclAfterInvocationProvider
delivers a solution,
and is configured as follows:
<bean id="afterAclRead" class="org.springframework.security.afterinvocation.AclEntryAfterInvocationProvider"> <constructor-arg ref="aclService"/> <constructor-arg> <list> <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/> <ref local="org.springframework.security.acls.domain.BasePermission.READ"/> </list> </constructor-arg> </bean>
In the above example, the Contact
will be
retrieved and passed to the
AclEntryAfterInvocationProvider
. The provider
will thrown an AccessDeniedException
if one of
the listed requirePermission
s is not held by the
Authentication
. The
AclEntryAfterInvocationProvider
queries the
Acl
Service to determine the ACL that applies for
this domain object to this Authentication
.
Similar to the
AclEntryAfterInvocationProvider
is
AclEntryAfterInvocationCollectionFilteringProvider
.
It is designed to remove Collection
or array
elements for which a principal does not have access. It never thrown
an AccessDeniedException
- simply silently
removes the offending elements. The provider is configured as
follows:
<bean id="afterAclCollectionRead" class="org.springframework.security.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider"> <constructor-arg ref="aclService"/> <constructor-arg> <list> <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/> <ref local="org.springframework.security.acls.domain.BasePermission.READ"/> </list> </constructor-arg> </bean>
As you can imagine, the returned Object
must be a Collection
or array for this provider
to operate. It will remove any element if the
AclManager
indicates the
Authentication
does not hold one of the listed
requirePermission
s.
The Contacts sample application demonstrates these two
AfterInvocationProvider
s.
PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new
ACL module. The new ACL module is a significant rewrite of the
existing ACL module. The new module can be found under the
org.springframework.security.acls
package, with
the old ACL module under
org.springframework.security.acl
. We encourage
users to consider testing with the new ACL module and build
applications with it. The old ACL module should be considered
deprecated and may be removed from a future release.
A common services layer method we've all written at one stage or another looks like this:
public Contact getById(Integer id);
Quite often, only principals with permission to read the
Contact
should be allowed to obtain it. In this
situation the AccessDecisionManager
approach
provided by the AbstractSecurityInterceptor
will
not suffice. This is because the identity of the
Contact
is all that is available before the
secure object is invoked. The
BasicAclAfterInvocationProvider
delivers a
solution, and is configured as follows:
<bean id="afterAclRead" class="org.springframework.security.afterinvocation.BasicAclEntryAfterInvocationProvider"> <property name="aclManager" ref="aclManager"/> <property name="requirePermission"> <list> <ref local="org.springframework.security.acl.basic.SimpleAclEntry.ADMINISTRATION"/> <ref local="org.springframework.security.acl.basic.SimpleAclEntry.READ"/> </list> </property> </bean>
In the above example, the Contact
will be
retrieved and passed to the
BasicAclEntryAfterInvocationProvider
. The
provider will thrown an AccessDeniedException
if
one of the listed requirePermission
s is not held
by the Authentication
. The
BasicAclEntryAfterInvocationProvider
queries the
AclManager
to determine the ACL that applies for
this domain object to this Authentication
.
Similar to the
BasicAclEntryAfterInvocationProvider
is
BasicAclEntryAfterInvocationCollectionFilteringProvider
.
It is designed to remove Collection
or array
elements for which a principal does not have access. It never thrown
an AccessDeniedException
- simply silently
removes the offending elements. The provider is configured as
follows:
<bean id="afterAclCollectionRead" class="org.springframework.security.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider"> <property name="aclManager" ref="aclManager"/> <property name="requirePermission"> <list> <ref local="org.springframework.security.acl.basic.SimpleAclEntry.ADMINISTRATION"/> <ref local="org.springframework.security.acl.basic.SimpleAclEntry.READ"/> </list> </property> </bean>
As you can imagine, the returned Object
must be a Collection
or array for this provider
to operate. It will remove any element if the
AclManager
indicates the
Authentication
does not hold one of the listed
requirePermission
s.
The Contacts sample application demonstrates these two
AfterInvocationProvider
s.
AuthorizeTag
is used to include content if
the current principal holds certain
GrantedAuthority
s.
The following JSP fragment illustrates how to use the
AuthorizeTag
:
<security:authorize ifAllGranted="ROLE_SUPERVISOR"> <td> <a href="del.htm?id=<c:out value="${contact.id}"/>">Del</a> </td> </security:authorize>
This tag would cause the tag's body to be output if the principal has been granted ROLE_SUPERVISOR.
The security:authorize
tag declares the
following attributes:
ifAllGranted
: All the listed roles must
be granted for the tag to output its body.
ifAnyGranted
: Any of the listed roles
must be granted for the tag to output its body.
ifNotGranted
: None of the listed roles
must be granted for the tag to output its body.
You'll note that in each attribute you can list multiple roles.
Simply separate the roles using a comma. The
authorize
tag ignores whitespace in
attributes.
The tag library logically ANDs all of it's parameters together.
This means that if you combine two or more attributes, all attributes
must be true for the tag to output it's body. Don't add an
ifAllGranted="ROLE_SUPERVISOR"
, followed by an
ifNotGranted="ROLE_SUPERVISOR"
, or you'll be
surprised to never see the tag's body.
By requiring all attributes to return true, the authorize tag
allows you to create more complex authorization scenarios. For
example, you could declare an
ifAllGranted="ROLE_SUPERVISOR"
and an
ifNotGranted="ROLE_NEWBIE_SUPERVISOR"
in the same
tag, in order to prevent new supervisors from seeing the tag body.
However it would no doubt be simpler to use
ifAllGranted="ROLE_EXPERIENCED_SUPERVISOR"
rather
than inserting NOT conditions into your design.
One last item: the tag verifies the authorizations in a specific
order: first ifNotGranted
, then
ifAllGranted
, and finally, if
AnyGranted
.
AccessControlListTag
is used to include
content if the current principal has an ACL to the indicated domain
object.
The following JSP fragment illustrates how to use the
AccessControlListTag
:
<security:accesscontrollist domainObject="${contact}" hasPermission="8,16"> <td><a href="<c:url value="del.htm"><c:param name="contactId" value="${contact.id}"/></c:url>">Del</a></td> </security:accesscontrollist>
This tag would cause the tag's body to be output if the
principal holds either permission 16 or permission 1 for the "contact"
domain object. The numbers are actually integers that are used with
BasePermission
bit masking. Please refer to the ACL
section of this reference guide to understand more about the ACL
capabilities of Spring Security.
AclTag
is part of the old ACL module and
should be considered deprecated. For the sake of historical reference,
works exactly the samae as
AccessControlListTag
.