Table of Contents
PLEASE NOTE: Before release 2.0.0, Spring Security was known as Acegi Security. An ACL
module was provided with the old Acegi Security releases under the
org.[acegisecurity/springsecurity].acl
package. This old package
is now deprecated and will be removed in a future release of Spring Security. This
chapter covers the new ACL module, which is officially recommended from Spring Security
2.0.0 and above, and can be found under the
org.springframework.security.acls
package.
Complex applications often will find the need to define access permissions not simply
at a web request or method invocation level. Instead, security decisions need to
comprise both who (Authentication
), where
(MethodInvocation
) and what (SomeDomainObject
). In
other words, authorization decisions also need to consider the actual domain object
instance subject of a method invocation.
Imagine you're designing an application for a pet clinic. There will be two main groups of users of your Spring-based application: staff of the pet clinic, as well as the pet clinic's customers. The staff will have access to all of the data, whilst your customers will only be able to see their own customer records. To make it a little more interesting, your customers can allow other users to see their customer records, such as their "puppy preschool" mentor or president of their local "Pony Club". Using Spring Security as the foundation, you have several approaches that can be used:
Write your business methods to enforce the security. You could consult a
collection within the Customer
domain object instance to
determine which users have access. By using the
SecurityContextHolder.getContext().getAuthentication()
,
you'll be able to access the Authentication
object.
Write an AccessDecisionVoter
to enforce the security
from the GrantedAuthority[]
s stored in the
Authentication
object. This would mean your
AuthenticationManager
would need to populate the
Authentication
with custom
GrantedAuthority
[]s representing each of the
Customer
domain object instances the principal has
access to.
Write an AccessDecisionVoter
to enforce the security
and open the target Customer
domain object directly. This
would mean your voter needs access to a DAO that allows it to retrieve the
Customer
object. It would then access the
Customer
object's collection of approved users and
make the appropriate decision.
Each one of these approaches is perfectly legitimate. However, the first couples your
authorization checking to your business code. The main problems with this include the
enhanced difficulty of unit testing and the fact it would be more difficult to reuse the
Customer
authorization logic elsewhere. Obtaining the
GrantedAuthority[]
s from the Authentication
object is also fine, but will not scale to large numbers of
Customer
s. If a user might be able to access 5,000
Customer
s (unlikely in this case, but imagine if it were a popular
vet for a large Pony Club!) the amount of memory consumed and time required to construct
the Authentication
object would be undesirable. The final method,
opening the Customer
directly from external code, is probably the
best of the three. It achieves separation of concerns, and doesn't misuse memory or CPU
cycles, but it is still inefficient in that both the
AccessDecisionVoter
and the eventual business method itself will
perform a call to the DAO responsible for retrieving the Customer
object. Two accesses per method invocation is clearly undesirable. In addition, with
every approach listed you'll need to write your own access control list (ACL)
persistence and business logic from scratch.
Fortunately, there is another alternative, which we'll talk about below.