From 48add4053844069d6d5fb489930ef93ab69f29ee Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Sat, 24 Feb 2007 22:40:46 +0000 Subject: [PATCH] Docbook source and resources copied for Maven 2 docbook plugin. --- src/docbook/acegi.xml | 6323 +++++++++++++++++ src/docbook/resources/images/ACLSecurity.gif | Bin 0 -> 4041 bytes .../resources/images/AccessDecisionVoting.gif | Bin 0 -> 6481 bytes .../resources/images/AfterInvocation.gif | Bin 0 -> 4672 bytes .../resources/images/Authentication.gif | Bin 0 -> 12955 bytes .../resources/images/BasicAclProvider.gif | Bin 0 -> 9921 bytes src/docbook/resources/images/Context.gif | Bin 0 -> 4269 bytes src/docbook/resources/images/Permissions.gif | Bin 0 -> 14168 bytes .../resources/images/SecurityInterception.gif | Bin 0 -> 5532 bytes src/docbook/resources/images/logo.gif | Bin 0 -> 8919 bytes src/docbook/resources/images/logo.psd | Bin 0 -> 149793 bytes 11 files changed, 6323 insertions(+) create mode 100644 src/docbook/acegi.xml create mode 100644 src/docbook/resources/images/ACLSecurity.gif create mode 100644 src/docbook/resources/images/AccessDecisionVoting.gif create mode 100644 src/docbook/resources/images/AfterInvocation.gif create mode 100644 src/docbook/resources/images/Authentication.gif create mode 100644 src/docbook/resources/images/BasicAclProvider.gif create mode 100644 src/docbook/resources/images/Context.gif create mode 100644 src/docbook/resources/images/Permissions.gif create mode 100644 src/docbook/resources/images/SecurityInterception.gif create mode 100644 src/docbook/resources/images/logo.gif create mode 100644 src/docbook/resources/images/logo.psd diff --git a/src/docbook/acegi.xml b/src/docbook/acegi.xml new file mode 100644 index 0000000000..dece544dbc --- /dev/null +++ b/src/docbook/acegi.xml @@ -0,0 +1,6323 @@ + + + + + + Acegi Security + + Reference Documentation + + 1.0.4 + + + + Ben + + Alex + + + + + + + + Preface + + Acegi Security provides a comprehensive security solution for + J2EE-based enterprise software applications. As you will discover as you + venture through this reference guide, we have tried to provide you a + useful and highly configurable security system. + + Security is an ever-moving target, and it's important to pursue a + comprehensive, system-wide approach. In security circles we encourage you + to adopt "layers of security", so that each layer tries to be as secure as + possible in its own right, with successive layers providing additional + security. The "tighter" the security of each layer, the more robust and + safe your application will be. At the bottom level you'll need to deal + with issues such as transport security and system identification, in order + to mitigate man-in-the-middle attacks. Next you'll generally utilise + firewalls, perhaps with VPNs or IP security to ensure only authorised + systems can attempt to connect. In corporate environments you may deploy a + DMZ to separate public-facing servers from backend database and + application servers. Your operating system will also play a critical part, + addressing issues such as running processes as non-privileged users and + maximising file system security. An operating system will usually also be + configured with its own firewall. Hopefully somewhere along the way you'll + be trying to prevent denial of service and brute force attacks against the + system. An intrusion detection system will also be especially useful for + monitoring and responding to attacks, with such systems able to take + protective action such as blocking offending TCP/IP addresses in + real-time. Moving to the higher layers, your Java Virtual Machine will + hopefully be configured to minimize the permissions granted to different + Java types, and then your application will add its own problem + domain-specific security configuration. Acegi Security makes this latter + area - application security - much easier. + + Of course, you will need to properly address all security layers + mentioned above, together with managerial factors that encompass every + layer. A non-exhaustive list of such managerial factors would include + security bulletin monitoring, patching, personnel vetting, audits, change + control, engineering management systems, data backup, disaster recovery, + performance benchmarking, load monitoring, centralised logging, incident + response procedures etc. + + With Acegi Security being focused on helping you with the enterprise + application security layer, you will find that there are as many different + requirements as there are business problem domains. A banking application + has different needs from an ecommerce application. An ecommerce + application has different needs from a corporate sales force automation + tool. These custom requirements make application security interesting, + challenging and rewarding. + + This reference guide has been largely restructured for the 1.0.0 + release of Acegi Security. Please read Part I, Overall Architecture, in its + entirety. The remaining parts of the reference guide are structured in a + more traditional reference style, designed to be read on an as-required + basis. + + We hope that you find this reference guide useful, and we welcome + your feedback and suggestions. + + Finally, welcome to the Acegi Security community. + + + + Overall Architecture + + + Like most software, Acegi Security has certain central interfaces, + classes and conceptual abstractions that are commonly used throughout + the framework. In this part of the reference guide we will introduce + Acegi Security, before examining these central elements that are + necessary to successfully planning and executing an Acegi Security + integration. + + + + Introduction + + + What is Acegi Security? + + Acegi Security provides comprehensive security services for + J2EE-based enterprise software applications. There is a particular + emphasis on supporting projects built using The Spring Framework, + which is the leading J2EE solution for enterprise software + development. If you're not using Spring for developing enterprise + applications, we warmly encourage you to take a closer look at it. + Some familiarity with Spring - and in particular dependency injection + principles - will help you get up to speed with Acegi Security more + easily. + + People use Acegi Security for many reasons, but most are drawn + to the project after finding the security features of J2EE's Servlet + Specification or EJB Specification lack the depth required for typical + enterprise application scenarios. Whilst mentioning these standards, + it's important to recognise that they are not portable at a WAR or EAR + level. Therefore, if you switch server environments, it is typically a + lot of work to reconfigure your application's security in the new + target environment. Using Acegi Security overcomes these problems, and + also brings you dozens of other useful, entirely customisable security + features. + + As you probably know, security comprises two major operations. + The first is known as "authentication", which is the process of + establishing a principal is who they claim to be. A "principal" + generally means a user, device or some other system which can perform + an action in your application. "Authorization" refers to the process + of deciding whether a principal is allowed to perform an action in + your application. To arrive at the point where an authorization + decision is needed, the identity of the principal has already been + established by the authentication process. These concepts are common, + and not at all specific to Acegi Security. + + At an authentication level, Acegi Security supports a wide range + of authentication models. Most of these authentication models are + either provided by third parties, or are developed by relevant + standards bodies such as the Internet Engineering Task Force. In + addition, Acegi Security provides its own set of authentication + features. Specifically, Acegi Security currently supports + authentication with all of these technologies: + + + + HTTP BASIC authentication headers (an IEFT RFC-based + standard) + + + + HTTP Digest authentication headers (an IEFT RFC-based + standard) + + + + HTTP X.509 client certificate exchange (an IEFT RFC-based + standard) + + + + LDAP (a very common approach to cross-platform + authentication needs, especially in large environments) + + + + Form-based authentication (for simple user interface + needs) + + + + Computer Associates Siteminder + + + + JA-SIG Central Authentication Service (otherwise known as + CAS, which is a popular open source single sign on system) + + + + Transparent authentication context propagation for Remote + Method Invocation (RMI) and HttpInvoker (a Spring remoting + protocol) + + + + Automatic "remember-me" authentication (so you can tick a + box to avoid re-authentication for a predetermined period of + time) + + + + Anonymous authentication (allowing every call to + automatically assume a particular security identity) + + + + Run-as authentication (which is useful if one call should + proceed with a different security identity) + + + + Java Authentication and Authorization Service (JAAS) + + + + Container integration with JBoss, Jetty, Resin and Tomcat + (so you can still use Container Manager Authentication if + desired) + + + + Your own authentication systems (see below) + + + + Many independent software vendors (ISVs) adopt Acegi Security + because of this rich choice of authentication models. Doing so allows + them to quickly integrate their solutions with whatever their end + clients need, without undertaking a lot of engineering or requiring + the client to change their environment. If none of the above + authentication mechanisms suit your needs, Acegi Security is an open + platform and it is quite simple to write your own authentication + mechanism. Many corporate users of Acegi Security need to integrate + with "legacy" systems that don't follow any particular security + standards, and Acegi Security is happy to "play nicely" with such + systems. + + Sometimes the mere process of authentication isn't enough. + Sometimes you need to also differentiate security based on the way a + principal is interacting with your application. For example, you might + want to ensure requests only arrive over HTTPS, in order to protect + passwords from eavesdropping or end users from man-in-the-middle + attacks. Or, you might want to ensure that an actual human being is + making the requests and not some robot or other automated process. + This is especially helpful to protect password recovery processes from + brute force attacks, or simply to make it harder for people to + duplicate your application's key content. To help you achieve these + goals, Acegi Security fully supports automatic "channel security", + together with JCaptcha integration for human user detection. + + Irrespective of how authentication was undertaken, Acegi + Security provides a deep set of authorization capabilities. There are + three main areas of interest in respect of authorization, these being + authorizing web requests, authorizing methods can be invoked, and + authorizing access to individual domain object instances. To help you + understand the differences, consider the authorization capabilities + found in the Servlet Specification web pattern security, EJB Container + Managed Security and file system security respectively. Acegi Security + provides deep capabilities in all of these important areas, which + we'll explore later in this reference guide. + + + + History + + Acegi Security began in late 2003, when a question was posed on + the Spring Developers' mailing list asking whether there had been any + consideration given to a Spring-based security implementation. At the + time the Spring community was relatively small (especially by today's + size!), and indeed Spring itself had only existed as a SourceForge + project from early 2003. The response to the question was that it was + a worthwhile area, although a lack of time currently prevented its + exploration. + + With that in mind, a simple security implementation was built + and not released. A few weeks later another member of the Spring + community inquired about security, and at the time this code was + offered to them. Several other requests followed, and by January 2004 + around twenty people were using the code. These pioneering users were + joined by others who suggested a SourceForge project was in order, + which was duly established in March 2004. + + In those early days, the project didn't have any of its own + authentication modules. Container Managed Security was relied upon for + the authentication process, with Acegi Security instead focusing on + authorization. This was suitable at first, but as more and more users + requested additional container support, the fundamental limitation of + container-specific authentication realm interfaces was experienced. + There was also a related issue of adding new JARs to the container's + classpath, which was a common source of end user confusion and + misconfiguration. + + Acegi Security-specific authentication services were + subsequently introduced. Around a year later, the Acegi Security + became an official Spring Framework subproject. The 1.0.0 final + release was published in May 2006 - after more than two and a half + years of active use in numerous production software projects and many + hundreds of improvements and community contributions. + + Today Acegi Security enjoys a strong and active open source + community. There are thousands of messages about Acegi Security on the + support forums. Fourteen developers work on the code itself, with an + active community who also regularly share patches and support their + peers. + + + + Release Numbering + + It is useful to understand how Acegi Security release numbers + work, as it will help you identify the effort (or lack thereof) + involved in migrating to future releases of the project. Officially, + we use the Apache Portable Runtime Project versioning guidelines, + which can be viewed at + http://apr.apache.org/versioning.html. We quote the + introduction contained on that page for your convenience: + + Versions are denoted using a standard triplet of + integers: MAJOR.MINOR.PATCH. The basic intent is that MAJOR versions + are incompatible, large-scale upgrades of the API. MINOR versions + retain source and binary compatibility with older minor versions, and + changes in the PATCH level are perfectly compatible, forwards and + backwards. + + + + + Technical Overview + + + Runtime Environment + + Acegi Security is written to execute within a standard Java 1.3 + Runtime Environment. It also supports Java 5.0, although the Java + types which are specific to this release are packaged in a separate + package with the suffix "tiger" in their JAR filename. As Acegi + Security aims to operate in a self-contained manner, there is no need + to place any special configuration files into your Java Runtime + Environment. In particular, there is no need to configure a special + Java Authentication and Authorization Service (JAAS) policy file or + place Acegi Security into common classpath locations. + + Similarly, if you are using an EJB Container or Servlet + Container there is no need to put any special configuration files + anywhere, nor include Acegi Security in a server classloader. + + This above design offers maximum deployment time flexibility, as + you can simply copy your target artifact (be it a JAR, WAR or EAR) + from one system to another and it will immediately work. + + + + Shared Components + + Let's explore some of the most important shared components in + Acegi Security. Components are considered "shared" if they are central + to the framework and the framework cannot operate without them. These + Java types represent the building blocks of the remaining system, so + it's important to understand that they're there, even if you don't + need to directly interact with them. + + The most fundamental object is + SecurityContextHolder. This is where we store + details of the present security context of the application, which + includes details of the principal currently using the application. By + default the SecurityContextHolder uses a + ThreadLocal to store these details, which means + that the security context is always available to methods in the same + thread of execution, even if the security context is not explicitly + passed around as an argument those methods. Using a + ThreadLocal in this way is quite safe if care is + taken to clear the thread after the present principal's request is + processed. Of course, Acegi Security takes care of for you + automatically so there is no need to worry about it. + + Some applications aren't entirely suitable for using a + ThreadLocal, because of the specific way they work + with threads. For example, a Swing client might want all threads in a + Java Virtual Machine to use the same security context. For this + situation you would use the + SecurityContextHolder.MODE_GLOBAL. Other + applications might want to have threads spawned by the secure thread + also assume the same security identity. This is achieved by using + SecurityContextHolder.MODE_INHERITABLETHREADLOCAL. + You can change the mode from the default + SecurityContextHolder.MODE_THREADLOCAL in two ways. + The first is to set a system property. Alternatively, call a static + method on SecurityContextHolder. Most applications + won't need to change from the default, but if you do, take a look at + the JavaDocs for SecurityContextHolder to learn + more. + + Inside the SecurityContextHolder we store + details of the principal currently interacting with the application. + Acegi Security uses an Authentication object to + represent this information. Whilst you won't normally need to create + an Authentication object yourself, it is fairly + common for users to query the Authentication + object. You can use the following code block - from anywhere in your + application - to do this: + + Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + +if (obj instanceof UserDetails) { + String username = ((UserDetails)obj).getUsername(); +} else { + String username = obj.toString(); +} + + The above code introduces a number of interesting relationships + and key objects. First, you will notice that there is an intermediate + object between SecurityContextHolder and + Authentication. The + SecurityContextHolder.getContext() method is + actually returning a SecurityContext. Acegi + Security uses a few different SecurityContext + implementations, such as if we need to store special information + related to a request that is not principal-specific. A good example of + this is our JCaptcha integration, which needs to know whether the + current request came from a human user or not. Because such a decision + has nothing at all to do with the principal the request may or may not + be authenticated as, we store it in the + SecurityContext. + + Another item to note from the above code fragment is that you + can obtain a principal from the Authentication + object. The principal is just an Object. Most of + the time this can be cast into a UserDetails + object. UserDetails is a central interface in Acegi + Security. It represents a principal, but in an extensible and + application-specific way. Think of UserDetails as + the adapter between your own user database and what Acegi Security + needs inside the SecurityContextHolder. Being a + representation of something from your own user database, quite often + you will cast the UserDetails to the original + object that your application provided, so you can call + business-specific methods (like getEmail(), + getEmployeeNumber() and so on). + + By now you're probably wondering, so when do I provide a + UserDetails object? How do I do that? I thought you + said this thing was declarative and I didn't need to write any Java + code - what gives? The short answer is that there is a special + interface called UserDetailsService. The only + method on this interface accepts a String-based + username argument and returns a UserDetails. Most + authentication providers that ship with Acegi Security delegate to a + UserDetailsService as part of the authentication + process. The UserDetailsService is used to build + the Authentication object that is stored in the + SecurityContextHolder. The good news is that we + provide a number of UserDetailsService + implementations, including one that uses an in-memory map and another + that uses JDBC. Most users tends to write their own, though, with such + implementations often simply sitting on top of an existing Data Access + Object (DAO) that represents their employees, customers, or other + users of the enterprise application. Remember the advantage that + whatever your UserDetailsService returns can always be obtained from + the SecurityContextHolder, as per the above code + fragment. + + Besides the principal, another important method provided by + Authentication is + getAuthorities(). This method provides an array of + GrantedAuthority objects. A + GrantedAuthority is, not surprisingly, an authority + that is granted to the principal. Such authorities are usually + "roles", such as ROLE_ADMINISTRATOR or + ROLE_HR_SUPERVISOR. These roles are later on + configured for web authorization, method authorization and domain + object authorization. Other parts of Acegi Security are capable of + interpreting these authorities, and expect them to be present. You + will usually return GrantedAuthority objects from + the UserDetailsService. + + Usually the GrantedAuthority objects are + application-wide permissions. They are not specific to a given domain + object. Thus, you wouldn't likely have a + GrantedAuthority to represent a permission to + Employee object number 54, because if there are + thousands of such authorities you would quickly run out of memory (or, + at the very least, cause the application to take a long time to + authenticate a user). Of course, Acegi Security is expressly designed + to handle this common requirement, but you'd instead use the project's + domain object security capabilities for this purpose. + + Last but not least, sometimes you will need to store the + SecurityContext between HTTP requests. Other times + the principal will re-authenticate on every request, although most of + the time it will be stored. The + HttpSessionContextIntegrationFilter is responsible + for storing a SecurityContext between HTTP + requests. As suggested by the name of the class, the + HttpSession is used to store this information. You + should never interact directly with the HttpSession + for security purposes. There is simply no justification for doing so - + always use the SecurityContextHolder + instead. + + Just to recap, the major building blocks of Acegi Security + are: + + + + SecurityContextHolder, to provide any + type access to the SecurityContext. + + + + SecurityContext, to hold the + Authentication and possibly request-specific + security information. + + + + HttpSessionContextIntegrationFilter, to + store the SecurityContext in the + HttpSession between web requests. + + + + Authentication, to represent the + principal in an Acegi Security-specific manner. + + + + GrantedAuthority, to reflect the + application-wide permissions granted to a principal. + + + + UserDetails, to provide the necessary + information to build an Authentication object from your + application's DAOs. + + + + UserDetailsService, to create a + UserDetails when passed in a + String-based username (or certificate ID or + alike). + + + + Now that you've gained an understanding of these repeatedly-used + components, let's take a closer look at the process of + authentication. + + + + Authentication + + As mentioned in the beginning of this reference guide, Acegi + Security can participate in many different authentication + environments. Whilst we recommend people use Acegi Security for + authentication and not integrate with existing Container Managed + Authentication, it is nevertheless supported - as is integrating with + your own proprietary authentication system. Let's first explore + authentication from the perspective of Acegi Security managing web + security entirely on its own, which is illustrative of the most + complex and most common situation. + + Consider a typical web application's authentication + process: + + + + You visit the home page, and click on a link. + + + + A request goes to the server, and the server decides that + you've asked for a protected resource. + + + + As you're not presently authenticated, the server sends back + a response indicating that you must authenticate. The response + will either be a HTTP response code, or a redirect to a particular + web page. + + + + Depending on the authentication mechanism, your browser will + either redirect to the specific web page so that you can fill out + the form, or the browser will somehow retrieve your identity (eg a + BASIC authentication dialogue box, a cookie, a X509 certificate + etc). + + + + The browser will send back a response to the server. This + will either be a HTTP POST containing the contents of the form + that you filled out, or a HTTP header containing your + authentication details. + + + + Next the server will decide whether or not the presented + credentials are valid. If they're valid, the next step will + happen. If they're invalid, usually your browser will be asked to + try again (so you return to step two above). + + + + The original request that you made to cause the + authentication process will be retried. Hopefully you've + authenticated with sufficient granted authorities to access the + protected resource. If you have sufficient access, the request + will be successful. Otherwise, you'll receive back a HTTP error + code 403, which means "forbidden". + + + + Acegi Security has distinct classes responsible for most of the + steps described above. The main participants (in the order that they + are used) are the ExceptionTranslationFilter, an + AuthenticationEntryPoint, an authentication + mechanism, and an AuthenticationProvider. + + ExceptionTranslationFilter is an Acegi + Security filter that has responsibility for detecting any Acegi + Security exceptions that are thrown. Such exceptions will generally be + thrown by an AbstractSecurityInterceptor, which is + the main provider of authorization services. We will discuss + AbstractSecurityInterceptor in the next section, + but for now we just need to know that it produces Java exceptions and + knows nothing about HTTP or how to go about authenticating a + principal. Instead the ExceptionTranslationFilter + offers this service, with specific responsibility for either returning + error code 403 (if the principal has been authenticated and therefore + simply lacks sufficient access - as per step seven above), or + launching an AuthenticationEntryPoint (if the + principal has not been authenticated and therefore we need to go + commence step three). + + The AuthenticationEntryPoint is responsible + for step three in the above list. As you can imagine, each web + application will have a default authentication strategy (well, this + can be configured like nearly everything else in Acegi Security, but + let's keep it simple for now). Each major authentication system will + have its own AuthenticationEntryPoint + implementation, which takes actions such as described in step + three. + + After your browser decides to submit your authentication + credentials (either as a HTTP form post or HTTP header) there needs to + be something on the server that "collects" these authentication + details. By now we're at step six in the above list. In Acegi Security + was have a special name for the function of collecting authentication + details from a user agent (usually a web browser), and that name is + "authentication mechanism". After the authentication details are + collected from the user agent, an "Authentication + request" object is built and then presented to an + AuthenticationProvider. + + The last played in the Acegi Security authentication process is + an AuthenticationProvider. Quite simply, it is + responsible for taking an Authentication request + object and deciding whether or not it is valid. The provider will + either throw an exception, or return a fully populated + Authentication object. Remember our good friends, + UserDetails and + UserDetailsService? If not, head back to the + previous section and refresh your memory. Most + AuthenticationProviders will ask a + UserDetailsService to provide a + UserDetails object. As mentioned earlier, most + application will provide their own + UserDetailsService, although some will be able to + use the JDBC or in-memory implementation that ships with Acegi + Security. The resultant UserDetails object - and + particularly the GrantedAuthority[]s contained + within the UserDetails object - will be used when + building the fully populated Authentication + object. + + After the authentication mechanism receives back the + fully-populated Authentication object, it will deem + the request valid, put the Authentication into the + SecurityContextHolder, and cause the original + request to be retried (step seven above). If, on the other hand, the + AuthenticationProvider rejected the request, the + authentication mechanism will ask the user agent to retry (step two + above). + + Whilst this describes the typical authentication workflow, the + good news is that Acegi Security doesn't mind how you put an + Authentication inside the + SecurityContextHolder. The only critical + requirement is that the SecurityContextHolder + contains an Authentication that represents a + principal before the AbstractSecurityInterceptor + needs to authorize a request. + + You can (and many users do) write their own filters or MVC + controllers to provide interoperability with authentication systems + that are not based on Acegi Security. For example, you might be using + Container Managed Authentication which makes the current user + available from a ThreadLocal or JNDI location. Or you might work for a + company that has a legacy proprietary authentication system, which is + a corporate "standard" over which you have little control. In such + situations it's quite easy to get Acegi Security to work, and still + provide authorization capabilities. All you need to do is write a + filter (or equivalent) that reads the third-party user information + from a location, build an Acegi Security-specific Authentication + object, and put it onto the SecurityContextHolder. It's quite easy to + do this, and a fully-supported integration approach. + + + + Secure Objects + + If you're familiar with AOP, you'd be aware there are different + types of advice available: before, after, throws and around. An around + advice is very useful, because an advisor can elect whether or not to + proceed with a method invocation, whether or not to modify the + response, and whether or not to throw an exception. Acegi Security + provides an around advice for method invocations as well as web + requests. We achieve an around advice for method invocations using AOP + Alliance, and we achieve an around advice for web requests using a + standard Filter. + + For those not familiar with AOP, the key point to understand is + that Acegi Security can help you protect method invocations as well as + web requests. Most people are interested in securing method + invocations on their services layer. This is because the services + layer is where most business logic resides in current-generation J2EE + applications (for clarification, the author disapproves of this design + and instead advocates properly encapsulated domain objects together + with the DTO, assembly, facade and transparent persistence patterns, + but as anemic domain objects is the present mainstream approach, we'll + talk about it here). If you just need to secure method invocations to + the services layer, using the Spring's standard AOP platform + (otherwise known as AOP Alliance) will be adequate. If you need to + secure domain objects directly, you will likely find that AspectJ is + worth considering. + + You can elect to perform method authorization using AspectJ or + AOP Alliance, or you can elect to perform web request authorization + using filters. You can use zero, one, two or three of these approaches + together. The mainstream usage is to perform some web request + authorization, coupled with some AOP Alliance method invocation + authorization on the services layer. + + Acegi Security uses the term "secure object" to refer to any + object that can have security applies to it. Each secure object + supported by Acegi Security has its own class, which is a subclass of + AbstractSecurityInterceptor. Importantly, by the + time the AbstractSecurityInterceptor is run, the + SecurityContextHolder will contain a valid + Authentication if the principal has been + authenticated. + + The AbstractSecurityInterceptor provides a + consistent workflow for handling secure object requests. This workflow + includes looking up the "configuration attributes" associated with the + present request. A "configuration attribute" can be thought of as a + String that has special meaning to the classes used by + AbstractSecurityInterceptor. They're normally + configured against your AbstractSecurityInterceptor using XML. Anyway, + the AbstractSecurityInterceptor will ask an + AccessDecisionManager "here's the configuration + attributes, here's the current Authentication + object, and here's details of the current request - is this particular + principal allowed to perform this particular operation?". + + Assuming AccessDecisionManager decides to + allow the request, the AbstractSecurityInterceptor + will normally just proceed with the request. Having said that, on rare + occasions users may want to replace the + Authentication inside the + SecurityContext with a different + Authentication, which is handled by the + AccessDecisionManager calling a + RunAsManager. This might be useful in reasonably + unusual situations, such as if a services layer method needs to call a + remote system and present a different identity. Because Acegi Security + automatically propagates security identity from one server to another + (assuming you're using a properly-configured RMI or HttpInvoker + remoting protocol client), this may be useful. + + Following the secure object proceeding and then returning - + which may mean a method invocation completing or a filter chain + proceeding - the AbstractSecurityInterceptor gets + one final chance to handle the invocation. At this stage the + AbstractSecurityInterceptor is interested in + possibly modifying the return object. We might want this to happen + because an authorization decision couldn't be made "on the way in" to + a secure object invocation. Being highly pluggable, + AbstractSecurityInterceptor will pass control to an + AfterInvocationManager to actually modify the + object if needed. This class even can entirely replace the object, or + throw an exception, or not change it in any way. + + Because AbstractSecurityInterceptor is the + central template class, it seems fitting that the first figure should + be devoted to it. + + + + + + + + Figure 1: The key "secure object" model + + + + Only developers contemplating an entirely new way of + intercepting and authorizing requests would need to use secure objects + directly. For example, it would be possible to build a new secure + object to secure calls to a messaging system. Anything that requires + security and also provides a way of intercepting a call (like the AOP + around advice semantics) is capable of being made into a secure + object. Having said that, most Spring applications will simply use the + three currently supported secure object types (AOP Alliance + MethodInvocation, AspectJ + JoinPoint and web request + FilterInterceptor) with complete + transparency. + + + + Conclusion + + Congratulations! You have enough of a high-level picture of + Acegi Security to embark on your project. We've explored the shared + components, how authentication works, and reviewed the common + authorization concept of a "secure object". Everything that follows in + this reference guide may or may not apply to your particular needs, + and can be read in any order. + + + + + Supporting Infrastructure + + This chapter introduces some of the supplementary and supporting + infrastructure used by Acegi Security. If a capability is not directly + related to security, yet included in the Acegi Security project, we will + discuss it in this chapter. + + + Localization + + Acegi Security supports localization of exception messages that + end users are likely to see. If your application is designed for + English users, you don't need to do anything as by default all Acegi + Security messages are in English. If you need to support other + locales, everything you need to know is contained in this + section. + + All exception messages can be localized, including messages + related to authentication failures and access being denied + (authorization failures). Exceptions and logging that is focused on + developers or system deployers (including incorrect attributes, + interface contract violations, using incorrect constructors, startup + time validation, debug-level logging) etc are not localized and + instead are hard-coded in English within Acegi Security's code. + + Shipping in the acegi-security-xx.jar you + will find an org.acegisecurity package that in turn + contains a messages.properties file. This should be + referred to by your ApplicationContext, as Acegi + Security classes implement Spring's + MessageSourceAware interface and expect the message + resolver to be dependency injected at application context startup + time. Usually all you need to do is register a bean inside your + application context to refer to the messages. An example is shown + below: + + <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> + <property name="basename"><value>org/acegisecurity/messages</value></property> +</bean> + + The messages.properties is named in + accordance with standard resource bundles and represents the default + language supported by Acegi Securtiy messages. This default file is in + English. If you do not register a message source, Acegi Security will + still work correctly and fallback to hard-coded English versions of + the messages. + + If you wish to customize the + messages.properties file, or support other + languages, you should copy the file, rename it accordingly, and + register it inside the above bean definition. There are not a large + number of message keys inside this file, so localization should not be + considered a major initiative. If you do perform localization of this + file, please consider sharing your work with the community by logging + a JIRA task and attaching your appropriately-named localized version + of messages.properties. + + Rounding out the discussion on localization is the Spring + ThreadLocal known as + org.springframework.context.i18n.LocaleContextHolder. + You should set the LocaleContextHolder to represent + the preferred Locale of each user. Acegi Security + will attempt to locate a message from the message source using the + Locale obtained from this + ThreadLocal. Please refer to Spring documentation + for further details on using LocaleContextHolder + and the helper classes that can automatically set it for you (eg + AcceptHeaderLocaleResolver, + CookieLocaleResolver, + FixedLocaleResolver, + SessionLocaleResolver etc) + + + + Filters + + Acegi Security uses many filters, as referred to throughout the + remainder of this reference guide. You have a choice in how these + filters are added to your web application, in that you can use either + FilterToBeanProxy or + FilterChainProxy. We'll look at both below. + + Most filters are configured using the + FilterToBeanProxy. An example configuration from + web.xml follows: + + <filter> + <filter-name>Acegi HTTP Request Security Filter</filter-name> + <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class> + <init-param> + <param-name>targetClass</param-name> + <param-value>org.acegisecurity.ClassThatImplementsFilter</param-value> + </init-param> +</filter> + + Notice that the filter in web.xml is actually + a FilterToBeanProxy, and not the filter that will + actually implement the logic of the filter. What + FilterToBeanProxy does is delegate the + Filter's methods through to a bean which is + obtained from the Spring application context. This enables the bean to + benefit from the Spring application context lifecycle support and + configuration flexibility. The bean must implement + javax.servlet.Filter. + + The FilterToBeanProxy only requires a single + initialization parameter, targetClass or + targetBean. The targetClass + parameter locates the first object in the application context of the + specified class, whilst targetBean locates the + object by bean name. Like standard Spring web applications, the + FilterToBeanProxy accesses the application context + via + WebApplicationContextUtils.getWebApplicationContext(ServletContext), + so you should configure a ContextLoaderListener in + web.xml. + + There is a lifecycle issue to consider when hosting + Filters in an IoC container instead of a servlet + container. Specifically, which container should be responsible for + calling the Filter's "startup" and "shutdown" + methods? It is noted that the order of initialization and destruction + of a Filter can vary by servlet container, and this + can cause problems if one Filter depends on + configuration settings established by an earlier initialized + Filter. The Spring IoC container on the other hand + has more comprehensive lifecycle/IoC interfaces (such as + InitializingBean, + DisposableBean, BeanNameAware, + ApplicationContextAware and many others) as well as + a well-understood interface contract, predictable method invocation + ordering, autowiring support, and even options to avoid implementing + Spring interfaces (eg the destroy-method attribute + in Spring XML). For this reason we recommend the use of Spring + lifecycle services instead of servlet container lifecycle services + wherever possible. By default FilterToBeanProxy + will not delegate init(FilterConfig) and + destroy() methods through to the proxied bean. If + you do require such invocations to be delegated, set the + lifecycle initialization parameter to + servlet-container-managed. + + Rather than using FilterToBeanProxy, we + strongly recommend to use FilterChainProxy instead. + Whilst FilterToBeanProxy is a very useful class, + the problem is that the lines of code required for + <filter> and + <filter-mapping> entries in + web.xml explodes when using more than a few + filters. To overcome this issue, Acegi Security provides a + FilterChainProxy class. It is wired using a + FilterToBeanProxy (just like in the example above), + but the target class is + org.acegisecurity.util.FilterChainProxy. The filter + chain is then declared in the application context, using code such as + this: + + <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> + <property name="filterInvocationDefinitionSource"> + <value> + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT + /webServices/**=httpSessionContextIntegrationFilterWithASCFalse,basicProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor + /**=httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor + </value> + </property> +</bean> + + You may notice similarities with the way + FilterSecurityInterceptor is declared. Both regular + expressions and Ant Paths are supported, and the most specific URIs + appear first. At runtime the FilterChainProxy will + locate the first URI pattern that matches the current web request. + Each of the corresponding configuration attributes represent the name + of a bean defined in the application context. The filters will then be + invoked in the order they are specified, with standard + FilterChain behaviour being respected (a + Filter can elect not to proceed with the chain if + it wishes to end processing). + + As you can see, FilterChainProxy requires the + duplication of filter names for different request patterns (in the + above example, exceptionTranslationFilter and + filterSecurityInterceptor are duplicated). This + design decision was made to enable FilterChainProxy + to specify different Filter invocation orders for + different URI patterns, and also to improve both the expressiveness + (in terms of regular expressions, Ant Paths, and any custom + FilterInvocationDefinitionSource implementations) + and clarity of which Filters should be + invoked. + + You may have noticed we have declared two + HttpSessionContextIntegrationFilters in the filter + chain (ASC is short for + allowSessionCreation, a property of + HttpSessionContextIntegrationFilter). As web + services will never present a jsessionid on future + requests, creating HttpSessions for such user + agents would be wasteful. If you had a high-volume application which + required maximum scalability, we recommend you use the approach shown + above. For smaller applications, using a single + HttpSessionContextIntegrationFilter (with its + default allowSessionCreation as + true) would likely be sufficient. + + In relation to lifecycle issues, the + FilterChainProxy will always delegate + init(FilterConfig) and destroy() + methods through to the underlaying Filters if such + methods are called against FilterChainProxy itself. + In this case, FilterChainProxy guarantees to only + initialize and destroy each Filter once, + irrespective of how many times it is declared by the + FilterInvocationDefinitionSource. You control the + overall choice as to whether these methods are called or not via the + lifecycle initialization parameter of the + FilterToBeanProxy that proxies + FilterChainProxy. As discussed above, by default + any servlet container lifecycle invocations are not delegated through + to FilterChainProxy. + + The order that filters are defined in web.xml + is very important. Irrespective of which filters you are actually + using, the order of the <filter-mapping>s + should be as follows: + + + + ChannelProcessingFilter, because it might + need to redirect to a different protocol + + + + ConcurrentSessionFilter, because it + doesn't use any SecurityContextHolder + functionality but needs to update the + SessionRegistry to reflect ongoing requests + from the principal + + + + HttpSessionContextIntegrationFilter, so a + SecurityContext can be setup in the + SecurityContextHolder at the beginning of a web + request, and any changes to the SecurityContext + can be copied to the HttpSession when the web + request ends (ready for use with the next web request) + + + + Authentication processing mechanisms - + AuthenticationProcessingFilter, + CasProcessingFilter, + BasicProcessingFilter, HttpRequestIntegrationFilter, + JbossIntegrationFilter etc - so that the + SecurityContextHolder can be modified to + contain a valid Authentication request + token + + + + The + SecurityContextHolderAwareRequestFilter, if you + are using it to install an Acegi Security aware + HttpServletRequestWrapper into your servlet + container + + + + RememberMeProcessingFilter, so that if no + earlier authentication processing mechanism updated the + SecurityContextHolder, and the request presents + a cookie that enables remember-me services to take place, a + suitable remembered + Authentication object will + be put there + + + + AnonymousProcessingFilter, so that if no + earlier authentication processing mechanism updated the + SecurityContextHolder, an anonymous + Authentication object will be put there + + + + ExceptionTranslationFilter, to catch any + Acegi Security exceptions so that either a HTTP error response can + be returned or an appropriate + AuthenticationEntryPoint can be launched + + + + FilterSecurityInterceptor, to protect web + URIs + + + + All of the above filters use + FilterToBeanProxy or + FilterChainProxy. It is recommended that a single + FilterToBeanProxy proxy through to a single + FilterChainProxy for each application, with that + FilterChainProxy defining all of Acegi Security + Filters. + + If you're using SiteMesh, ensure Acegi Security filters execute + before the SiteMesh filters are called. This enables the + SecurityContextHolder to be populated in time for + use by SiteMesh decorators + + + + + Channel Security + + + Overview + + In addition to coordinating the authentication and authorization + requirements of your application, Acegi Security is also able to + ensure unauthenticated web requests have certain properties. These + properties may include being of a particular transport type, having a + particular HttpSession attribute set and so on. The + most common requirement is for your web requests to be received using + a particular transport protocol, such as HTTPS. + + An important issue in considering transport security is that of + session hijacking. Your web container manages a + HttpSession by reference to a + jsessionid that is sent to user agents either via a + cookie or URL rewriting. If the jsessionid is ever + sent over HTTP, there is a possibility that session identifier can be + intercepted and used to impersonate the user after they complete the + authentication process. This is because most web containers maintain + the same session identifier for a given user, even after they switch + from HTTP to HTTPS pages. + + If session hijacking is considered too significant a risk for + your particular application, the only option is to use HTTPS for every + request. This means the jsessionid is never sent + across an insecure channel. You will need to ensure your + web.xml-defined + <welcome-file> points to a HTTPS location, + and the application never directs the user to a HTTP location. Acegi + Security provides a solution to assist with the latter. + + + + Configuration + + To utilise Acegi Security's channel security services, add the + following lines to web.xml: + + +<filter> + <filter-name>Acegi Channel Processing Filter</filter-name> + <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class> + <init-param> + <param-name>targetClass</param-name> + <param-value>org.acegisecurity.securechannel.ChannelProcessingFilter</param-value> + </init-param> +</filter> + +<filter-mapping> + <filter-name>Acegi Channel Processing Filter</filter-name> + <url-pattern>/*</url-pattern> +</filter-mapping> + + + + As usual when running FilterToBeanProxy, you + will also need to configure the filter in your application + context: + + +<bean id="channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter"> + <property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property> + <property name="filterInvocationDefinitionSource"> + <value> + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + \A/secure/.*\Z=REQUIRES_SECURE_CHANNEL + \A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL + \A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL + \A.*\Z=REQUIRES_INSECURE_CHANNEL + </value> + </property> +</bean> + +<bean id="channelDecisionManager" class="org.acegisecurity.securechannel.ChannelDecisionManagerImpl"> + <property name="channelProcessors"> + <list> + <ref bean="secureChannelProcessor"/> + <ref bean="insecureChannelProcessor"/> + </list> + </property> +</bean> + +<bean id="secureChannelProcessor" class="org.acegisecurity.securechannel.SecureChannelProcessor"/> +<bean id="insecureChannelProcessor" class="org.acegisecurity.securechannel.InsecureChannelProcessor"/> + + + + Like FilterSecurityInterceptor, Apache Ant + style paths are also supported by the + ChannelProcessingFilter. + + The ChannelProcessingFilter operates by + filtering all web requests and determining the configuration + attributes that apply. It then delegates to the + ChannelDecisionManager. The default implementation, + ChannelDecisionManagerImpl, should suffice in most + cases. It simply delegates through the list of configured + ChannelProcessor instances. A + ChannelProcessor will review the request, and if it + is unhappy with the request (eg it was received across the incorrect + transport protocol), it will perform a redirect, throw an exception or + take whatever other action is appropriate. + + Included with Acegi Security are two concrete + ChannelProcessor implementations: + SecureChannelProcessor ensures requests with a + configuration attribute of REQUIRES_SECURE_CHANNEL + are received over HTTPS, whilst + InsecureChannelProcessor ensures requests with a + configuration attribute of + REQUIRES_INSECURE_CHANNEL are received over HTTP. + Both implementations delegate to a + ChannelEntryPoint if the required transport + protocol is not used. The two ChannelEntryPoint + implementations included with Acegi Security simply redirect the + request to HTTP and HTTPS as appropriate. Appropriate defaults are + assigned to the ChannelProcessor implementations + for the configuration attribute keywords they respond to and the + ChannelEntryPoint they delegate to, although you + have the ability to override these using the application + context. + + Note that the redirections are absolute (eg + http://www.company.com:8080/app/page), not relative + (eg /app/page). During testing it was discovered + that Internet Explorer 6 Service Pack 1 has a bug whereby it does not + respond correctly to a redirection instruction which also changes the + port to use. Accordingly, absolute URLs are used in conjunction with + bug detection logic in the PortResolverImpl that is + wired up by default to many Acegi Security beans. Please refer to the + JavaDocs for PortResolverImpl for further + details. + + You should note that using a secure channel is recommended if + usernames and passwords are to be kept secure during the login + process. If you do decide to use + ChannelProcessingFilter with form-based login, + please ensure that your login page is set to + REQUIRES_SECURE_CHANNEL, and that the + AuthenticationProcessingFilterEntryPoint.forceHttps + property is true. + + + + Conclusion + + Once configured, using the channel security filter is very easy. + Simply request pages without regard to the protocol (ie HTTP or HTTPS) + or port (eg 80, 8080, 443, 8443 etc). Obviously you'll still need a + way of making the initial request (probably via the + web.xml <welcome-file> or + a well-known home page URL), but once this is done the filter will + perform redirects as defined by your application context. + + You can also add your own ChannelProcessor + implementations to the ChannelDecisionManagerImpl. + For example, you might set a HttpSession attribute + when a human user is detected via a "enter the contents of this + graphic" procedure. Your ChannelProcessor would + respond to say REQUIRES_HUMAN_USER configuration + attributes and redirect to an appropriate entry point to start the + human user validation process if the HttpSession + attribute is not currently set. + + To decide whether a security check belongs in a + ChannelProcessor or an + AccessDecisionVoter, remember that the former is + designed to handle unauthenticated requests, whilst the latter is + designed to handle authenticated requests. The latter therefore has + access to the granted authorities of the authenticated principal. In + addition, problems detected by a ChannelProcessor + will generally cause a HTTP/HTTPS redirection so its requirements can + be met, whilst problems detected by an + AccessDecisionVoter will ultimately result in an + AccessDeniedException (depending on the governing + AccessDecisionManager). + + + + + Tag Libraries + + + Overview + + Acegi Security comes bundled with several JSP tag libraries that + eases JSP writing. The tag libraries are known as + authz and provide a range of different + services. + + + + Configuration + + All taglib classes are included in the core + acegi-security-xx.jar file, with the + authz.tld located in the JAR's + META-INF directory. This means for JSP 1.2+ web + containers you can simply include the JAR in the WAR's + WEB-INF/lib directory and it will be available. If + you're using a JSP 1.1 container, you'll need to declare the JSP + taglib in your web.xml file, and include + authz.tld in the WEB-INF/lib + directory. The following fragment is added to + web.xml: + + <taglib> + <taglib-uri>http://acegisecurity.org/authz</taglib-uri> + <taglib-location>/WEB-INF/authz.tld</taglib-location> +</taglib> + + + + Usage + + Now that you've configured the tag libraries, refer to the + individual reference guide sections for details on how to use + them. + + + + + + Authentication + + + In this part of the reference guide we will examine individual + authentication mechanisms and their corresponding + AuthenticationProviders. We'll also look at how to + configure authentication more generally, including if you have several + authentication approaches that need to be chained together. + + + + Common Authentication Services + + + Mechanisms, Providers and Entry Points + + If you're using Acegi Security-provided authentication + approaches, you'll usually need to configure a web filter, together + with an AuthenticationProvider and + AuthenticationEntryPoint. In this section we are + going to explore an example application that needs to support both + form-based authentication (ie so a nice HTML page is presented to a + user for them to login) plus BASIC authentication (ie so a web service + or similar can access protected resources). + + In the web.xml, this application will need a single Acegi + Security filter in order to use the FilterChainProxy. Nearly every + Acegi Security application will have such an entry, and it looks like + this: + + <filter> + <filter-name>Acegi Filter Chain Proxy</filter-name> + <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class> + <init-param> + <param-name>targetClass</param-name> + <param-value>org.acegisecurity.util.FilterChainProxy</param-value> + </init-param> +</filter> + +<filter-mapping> + <filter-name>Acegi Filter Chain Proxy</filter-name> + <url-pattern>/*</url-pattern> +</filter-mapping> + + The above declarations will cause every web request to be passed + through to Acegi Security's FilterChainProxy. As explained in the + filters section of this reference guide, the FilterChainProxy is a + generally-useful class that enables web requests to be passed to + different filters based on the URL patterns. Those delegated filters + are managed inside the application context, so they can benefit from + dependency injection. Let's have a look at what the FilterChainProxy + bean definition would look like inside your application + context: + + <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> + <property name="filterInvocationDefinitionSource"> + <value> + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT + /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,switchUserProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor + </value> + </property> +</bean> + + Internally Acegi Security will use a + PropertyEditor to convert the string presented in + the above XML fragment into a + FilterInvocationDefinitionSource object. What's + important to note at this stage is that a series of filters will be + run - in the order specified by the declaration - and each of those + filters are actually the <bean id> of another + bean inside the application context. So, in our case some extra beans + will also appear in the application context, and they'll be named + httpSessionContextIntegrationFilter, + logoutFilter and so on. The order that the filters + should appear is discussed in the filters section of the reference + guide - although they are correct in the above example. + + In our example we have the + AuthenticationProcessingFilter and + BasicProcessingFilter being used. These are the + "authentication mechanisms" that respond to form-based authentication + and BASIC HTTP header-based authentication respectively (we discussed + the role of authentication mechanisms earlier in this reference + guide). If you weren't using form or BASIC authentication, neither of + these beans would be defined. You'd instead define filters applicable + to your desired authentication environment, such as + DigestProcessingFilter or + CasProcessingFilter. Refer to the individual + chapters of this part of the reference guide to learn how to configure + each of these authentication mechanisms. + + Recall that + HttpSessionContextIntegrationFilter keeps the + contents of the SecurityContext between invocations + inside a HTTP session. This means the authentication mechanisms are + only used once, being when the principal initially tries to + authenticate. The rest of the time the authentication mechanisms sit + there and silently pass the request through to the next filter in the + chain. That is a practical requirement due to the fact that few + authentication approaches present credentials on each and every call + (BASIC authentication being a notable exception), but what happens if + a principal's account gets cancelled or disabled or otherwise changed + (eg an increase or decrease in GrantedAuthority[]s) + after the initial authentication step? Let's look at how that is + handled now. + + The major authorization provider for secure objects has + previously been introduced as + AbstractSecurityInterceptor. This class needs to + have access to an AuthenticationManager. It also + has configurable settings to indicate whether an + Authentication object should be re-authenticated on + each secure object invocation. By default it just accepts any + Authentication inside the + SecurityContextHolder is authenticated if + Authentication.isAuthenticated() returns true. This + is great for performance, but not ideal if you want to ensure + up-to-the-moment authentication validity. For such cases you'll + probably want to set the + AbstractSecurityInterceptor.alwaysReauthenticate + property to true. + + You might be asking yourself, "what's this + AuthenticationManager?". We haven't explored it + before, but we have discussed the concept of an + AuthenticationProvider. Quite simply, an + AuthenticationManager is responsible for passing requests through a + chain of AuthenticationProviders. It's a little like the filter chain + we discussed earlier, although there are some differences. There is + only one AuthenticationManager implementation shipped with Acegi + Security, so let's look at how it's configured for the example we're + using in this chapter: + + <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> + <property name="providers"> + <list> + <ref local="daoAuthenticationProvider"/> + <ref local="anonymousAuthenticationProvider"/> + <ref local="rememberMeAuthenticationProvider"/> + </list> + </property> +</bean> + + It's probably worth mentioning at this point that your + authentication mechanisms (which are usually filters) are also + injected with a reference to the + AuthenticationManager. So both + AbstractSecurityInterceptor as well as the + authentication mechanisms will use the above + ProviderManager to poll a list of + AuthenticationProviders. + + In our example we have three providers. They are tried in the + order shown (which is implied by the use of a List + instead of a Set), with each provider able to + attempt authentication, or skip authentication by simply returning + null. If all implementations return null, the + ProviderManager will throw a suitable exception. If + you're interested in learning more about chaining providers, please + refer to the ProviderManager JavaDocs. + + The providers to use will sometimes be interchangeable with the + authentication mechanisms, whilst at other times they will depend on a + specific authentication mechanism. For example, the + DaoAuthenticationProvider just needs a string-based + username and password. Various authentication mechanisms result in the + collection of a string-based username and password, including (but not + limited to) BASIC and form authentication. Equally, some + authentication mechanisms create an authentication request object + which can only be interpreted by a single type of + AuthenticationProvider. An example of this + one-to-one mapping would be JA-SIG CAS, which uses the notion of a + service ticket which can therefore only be authenticated by + CasAuthenticationProvider. A further example of a + one-to-one mapping would be the LDAP authentication mechanism, which + can only be processed an the + LdapAuthenticationProvider. The specifics of such + relationships are detailed in the JavaDocs for each class, plus the + authentication approach-specific chapters of this reference guide. You + need not be terribly concerned about this implementation detail, + because if you forget to register a suitable provider, you'll simply + receive a ProviderNotFoundException when an attempt + to authenticate is made. + + After configuring the correct authentication mechanisms in the + FilterChainProxy, and ensuring that a corresponding + AuthenticationProvider is registered in the + ProviderManager, your last step is to configure an + AuthenticationEntryPoint. Recall that earlier we + discussed the role of ExceptionTranslationFilter, + which is used when HTTP-based requests should receive back a HTTP + header or HTTP redirect in order to start authentication. Continuing + on with our earlier example: + + <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> + <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property> + <property name="accessDeniedHandler"> + <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl"> + <property name="errorPage" value="/accessDenied.jsp"/> + </bean> + </property> +</bean> + +<bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> + <property name="loginFormUrl"><value>/acegilogin.jsp</value></property> + <property name="forceHttps"><value>false</value></property> +</bean> + + Notice that the ExceptionTranslationFilter + requires two collaborators. The first, + AccessDeniedHandlerImpl, uses a + RequestDispatcher forward to display the specified + access denied error page. We use a forward so that the + SecurityContextHolder still contains details of the + principal, which may be useful for display to the user (in old + releases of Acegi Security we relied upon the servlet container to + handle a 403 error message, which lacked this useful contextual + information). AccessDeniedHandlerImpl will also set + the HTTP header to 403, which is the official error code to indicate + access denied. In the case of the + AuthentionEntryPoint, here we're setting what + action we would like taken when an unauthenticated principal attempts + to perform a protected operation. Because in our example we're going + to be using form-based authentication, we specify + AuthenticationProcessinFilterEntryPoint and the URL + of the login page. Your application will usually only have one entry + point, and most authentication approaches define their own specific + AuthenticationEntryPoint. Details of which entry + point to use for each authentication approach is discussed in the + authentication approach-specific chapters of this reference + guide. + + + + UserDetails and Associated Types + + As mentioned in the first part of the reference guide, most + authentication providers take advantage of the + UserDetails and + UserDetailsService interfaces. The contract for + this latter interface consists of a single method: + + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException; + + The returned UserDetails is an interface that + provides getters that guarantee non-null provision of basic + authentication information such as the username, password, granted + authorities and whether the user is enabled or disabled. Most + authentication providers will use a + UserDetailsService, even if the username and + password are not actually used as part of the authentication decision. + Generally such provider will be using the returned + UserDetails object just for its + GrantedAuthority[] information, because some other + system (like LDAP or X509 or CAS etc) has undertaken the + responsibility of actually validating the credentials. + + A single concrete implementation of + UserDetails is provided with Acegi Security, being + the User class. Acegi Security users will need to + decide when writing their UserDetailsService what + concrete UserDetails class to return. In most cases + User will be used directly or subclassed, although + special circumstances (such as object relational mappers) may require + users to write their own UserDetails implementation + from scratch. This is not such an unusual situation, and users should + not hesitate to simply return their normal domain object that + represents a user of the system. This is especially common given that + UserDetails is often used to store additional + principal-related properties (such as their telephone number and email + address), so that they can be easily used by web views. + + Given UserDetailsService is so simple to + implement, it should be easy for users to retrieve authentication + information using a persistence strategy of their choice. Having said + that, Acegi Security does include a couple of useful base + implementations, which we'll look at below. + + + In-Memory Authentication + + Whilst it is easy to use create a custom + UserDetailsService implementation that extracts + information from a persistence engine of choice, many applications + do not require such complexity. This is particularly true if you're + undertaking a rapid prototype or just starting integrating Acegi + Security, when you don't really want to spend time configuring + databases or writing UserDetailsService + implementations. For this sort of situation, a simple option is to + configure the InMemoryDaoImpl + implementation: + + <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> + <property name="userMap"> + <value> + marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR + dianne=emu,ROLE_TELLER + scott=wombat,ROLE_TELLER + peter=opal,disabled,ROLE_TELLER + </value> + </property> +</bean> + + In the above example, the userMap property + contains each of the usernames, passwords, a list of granted + authorities and an optional enabled/disabled keyword. Commas are + used to delimit each token. The username must appear to the left of + the equals sign, and the password must be the first token to the + right of the equals sign. The enabled and + disabled keywords (case insensitive) may appear + in the second or any subsequent token. Any remaining tokens are + treated as granted authorities, which are created as + GrantedAuthorityImpl objects (this is just for + your reference - most application don't need custom + GrantedAuthority implementations, so using the + default implementation in this manner is just fine). Note that if a + user has no password and/or no granted authorities, the user will + not be created in the in-memory authentication repository. + + InMemoryDaoImpl also offers a + setUserProperties(Properties) method, which + allows you to externalise the + java.util.Properties in another Spring configured + bean or an external properties file. You might like to use Spring's + PropertiesFactoryBean, which is useful for + loading such external properties files. This setter might prove + useful for simple applications that have a larger number of users, + or deployment-time configuration changes, but do not wish to use a + full database for handling authentication details. + + + + JDBC Authentication + + Acegi Security also includes a + UserDetailsService that can obtain authentication + information from a JDBC data source. Internally Spring JDBC is used, + so it avoids the complexity of a fully-featured object relational + mapper (ORM) just to store user details. If your application does + use an ORM tool, you might prefer to write a custom + UserDetailsService to reuse the mapping files + you've probably already created. Returning to + JdbcDaoImpl, an example configuration is shown + below: + + <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> + <property name="driverClassName"><value>org.hsqldb.jdbcDriver</value></property> + <property name="url"><value>jdbc:hsqldb:hsql://localhost:9001</value></property> + <property name="username"><value>sa</value></property> + <property name="password"><value></value></property> +</bean> + +<bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"> + <property name="dataSource"><ref bean="dataSource"/></property> +</bean> + + You can use different relational database management systems + by modifying the DriverManagerDataSource shown + above. You can also use a global data source obtained from JNDI, as + per normal Spring options. Irrespective of the database used and how + a DataSource is obtained, a standard schema must + be used as indicated in dbinit.txt. You can + download this file from the Acegi Security web site. + + If you default schema is unsuitable for your needs, + JdbcDaoImpl provides two properties that allow + customisation of the SQL statements. You may also subclass the + JdbcDaoImpl if further customisation is + necessary. Please refer to the JavaDocs for details, although please + note that the class is not intended for complex custom subclasses. + If you have complex needs (such as a special schema or would like a + certain UserDetails implementation returned), + you'd be better off writing your own + UserDetailsService. The base implementation + provided with Acegi Security is intended for typical situations, and + does not offer infinite configuration flexibility. + + + + + Concurrent Session Handling + + Acegi Security is able to prevent a principal from concurrently + authenticating to the same application more than a specified number of + times. Many ISVs take advantage of this to enforce licensing, whilst + network administrators like this feature because it helps prevent + people from sharing login names. You can, for example, stop user + "Batman" from logging onto the web application from two different + sessions. + + To use concurrent session support, you'll need to add the + following to web.xml: + + <listener> + <listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class> +</listener> + + In addition, you will need to add the + org.acegisecurity.concurrent.ConcurrentSessionFilter + to your FilterChainProxy. The + ConcurrentSessionFilter requires two properties, + sessionRegistry, which generally points to an + instance of SessionRegistryImpl, and + expiredUrl, which points to the page to display + when a session has expired. + + The web.xml + HttpSessionEventPublisher causes an + ApplicationEvent to be published to the Spring + ApplicationContext every time a + HttpSession commences or terminates. This is + critical, as it allows the SessionRegistryImpl to + be notified when a session ends. + + You will also need to wire up the + ConcurrentSessionControllerImpl and refer to it + from your ProviderManager bean: + + <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> + <property name="providers"> + <!-- your providers go here --> + </property> + <property name="sessionController"><ref bean="concurrentSessionController"/></property> +</bean> + +<bean id="concurrentSessionController" class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl"> + <property name="maximumSessions"><value>1</value></property> + <property name="sessionRegistry"><ref local="sessionRegistry"/></property> +</bean> + +<bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl"/> + + + + Authentication Tag Libraries + + AuthenticationTag is used to simply output a + property of the current principal's + Authentication.getPrincipal() object to the web + page. + + The following JSP fragment illustrates how to use the + AuthenticationTag: + + <authz:authentication operation="username"/> + + This tag would cause the principal's name to be output. Here we + are assuming the Authentication.getPrincipal() is a + UserDetails object, which is generally the case + when using the typical + DaoAuthenticationProvider. + + + + + DAO Authentication Provider + + + Overview + + Acegi Security includes a production-quality + AuthenticationProvider implementation called + DaoAuthenticationProvider. This authentication + provider is compatible with all of the authentication mechanisms that + generate a UsernamePasswordAuthenticationToken, and + is probably the most commonly used provider in the framework. Like + most of the other authentication providers, the + DaoAuthenticationProvider leverages a UserDetailsService in order to + lookup the username, password and GrantedAuthority[]s. Unlike most of + the other authentication providers that leverage UserDetailsService, + this authentication provider actually requires the password to be + presented, and the provider will actually evaluate the validity or + otherwise of the password presented in an authentication request + object. + + + + Configuration + + Aside from adding DaoAuthenticationProvider to your + ProviderManager list (as discussed at the start of this part of the + reference guide), and ensuring a suitable authentication mechanism is + configured to present a UsernamePasswordAuthenticationToken, the + configuration of the provider itself is rather simple: + + <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> + <property name="userDetailsService"><ref bean="inMemoryDaoImpl"/></property> + <property name="saltSource"><ref bean="saltSource"/></property> + <property name="passwordEncoder"><ref bean="passwordEncoder"/></property> +</bean> + + The PasswordEncoder and + SaltSource are optional. A + PasswordEncoder provides encoding and decoding of + passwords presented in the UserDetails object that + is returned from the configured UserDetailsService. + A SaltSource enables the passwords to be populated + with a "salt", which enhances the security of the passwords in the + authentication repository. PasswordEncoder + implementations are provided with Acegi Security covering MD5, SHA and + cleartext encodings. Two SaltSource implementations + are also provided: SystemWideSaltSource which + encodes all passwords with the same salt, and + ReflectionSaltSource, which inspects a given + property of the returned UserDetails object to + obtain the salt. Please refer to the JavaDocs for further details on + these optional features. + + In addition to the properties above, the + DaoAuthenticationProvider supports optional caching + of UserDetails objects. The + UserCache interface enables the + DaoAuthenticationProvider to place a + UserDetails object into the cache, and retrieve it + from the cache upon subsequent authentication attempts for the same + username. By default the DaoAuthenticationProvider + uses the NullUserCache, which performs no caching. + A usable caching implementation is also provided, + EhCacheBasedUserCache, which is configured as + follows: + + <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> + <property name="userDetailsService"><ref bean="userDetailsService"/></property> + <property name="userCache"><ref bean="userCache"/></property> +</bean> + +<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> + <property name="configLocation"> + <value>classpath:/ehcache-failsafe.xml</value> + </property> +</bean> + +<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> + <property name="cacheManager"> + <ref local="cacheManager"/> + </property> + <property name="cacheName"> + <value>userCache</value> + </property> +</bean> + +<bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"> + <property name="cache"><ref local="userCacheBackend"/></property> +</bean> + + All Acegi Security EH-CACHE implementations (including + EhCacheBasedUserCache) require an EH-CACHE + Cache object. The Cache object + can be obtained from wherever you like, although we recommend you use + Spring's factory classes as shown in the above configuration. If using + Spring's factory classes, please refer to the Spring documentation for + further details on how to optimise the cache storage location, memory + usage, eviction policies, timeouts etc. + + A design decision was made not to support account locking in the + DaoAuthenticationProvider, as doing so would have + increased the complexity of the UserDetailsService + interface. For instance, a method would be required to increase the + count of unsuccessful authentication attempts. Such functionality + could be easily provided by leveraging the application event + publishing features discussed below. + + DaoAuthenticationProvider returns an + Authentication object which in turn has its + principal property set. The principal will be + either a String (which is essentially the username) + or a UserDetails object (which was looked up from + the UserDetailsService). By default the + UserDetails is returned, as this enables + applications to add extra properties potentially of use in + applications, such as the user's full name, email address etc. If + using container adapters, or if your applications were written to + operate with Strings (as was the case for releases + prior to Acegi Security 0.6), you should set the + DaoAuthenticationProvider.forcePrincipalAsString + property to true in your application context + + + + + Java Authentication and Authorization Service (JAAS) + Provider + + + Overview + + Acegi Security provides a package able to delegate + authentication requests to the Java Authentication and Authorization + Service (JAAS). This package is discussed in detail below. + + Central to JAAS operation are login configuration files. To + learn more about JAAS login configuration files, consult the JAAS + reference documentation available from Sun Microsystems. We expect you + to have a basic understanding of JAAS and its login configuration file + syntax in order to understand this section. + + + + Configuration + + The JaasAuthenticationProvider attempts to + authenticate a user’s principal and credentials through JAAS. + + Let’s assume we have a JAAS login configuration file, + /WEB-INF/login.conf, with the following + contents: + + JAASTest { + sample.SampleLoginModule required; +}; + + Like all Acegi Security beans, the + JaasAuthenticationProvider is configured via the + application context. The following definitions would correspond to the + above JAAS login configuration file: + + +<bean id="jaasAuthenticationProvider" class="org.acegisecurity.providers.jaas.JaasAuthenticationProvider"> + <property name="loginConfig"> + <value>/WEB-INF/login.conf</value> + </property> + <property name="loginContextName"> + <value>JAASTest</value> + </property> + <property name="callbackHandlers"> + <list> + <bean class="org.acegisecurity.providers.jaas.JaasNameCallbackHandler"/> + <bean class="org.acegisecurity.providers.jaas.JaasPasswordCallbackHandler"/> + </list> + </property> + <property name="authorityGranters"> + <list> + <bean class="org.acegisecurity.providers.jaas.TestAuthorityGranter"/> + </list> + </property> +</bean> + + + + The CallbackHandlers and + AuthorityGranters are discussed below. + + + JAAS CallbackHandler + + Most JAAS LoginModules require a callback + of some sort. These callbacks are usually used to obtain the + username and password from the user. + + In an Acegi Security deployment, Acegi Security is responsible + for this user interaction (via the authentication mechanism). Thus, + by the time the authentication request is delegated through to JAAS, + Acegi Security's authentication mechanism will already have + fully-populated an Authentication object + containing all the information required by the JAAS + LoginModule. + + Therefore, the JAAS package for Acegi Security provides two + default callback handlers, + JaasNameCallbackHandler and + JaasPasswordCallbackHandler. Each of these + callback handlers implement + JaasAuthenticationCallbackHandler. In most cases + these callback handlers can simply be used without understanding the + internal mechanics. + + For those needing full control over the callback behavior, + internally JaasAutheticationProvider wraps these + JaasAuthenticationCallbackHandlers with an + InternalCallbackHandler. The + InternalCallbackHandler is the class that + actually implements JAAS’ normal CallbackHandler + interface. Any time that the JAAS LoginModule is + used, it is passed a list of application context configured + InternalCallbackHandlers. If the + LoginModule requests a callback against the + InternalCallbackHandlers, the callback is in-turn + passed to the JaasAuthenticationCallbackHandlers + being wrapped. + + + + JAAS AuthorityGranter + + JAAS works with principals. Even "roles" are represented as + principals in JAAS. Acegi Security, on the other hand, works with + Authentication objects. Each + Authentication object contains a single + principal, and multiple GrantedAuthority[]s. To + facilitate mapping between these different concepts, Acegi + Security's JAAS package includes an + AuthorityGranter interface. + + An AuthorityGranter is responsible for + inspecting a JAAS principal and returning a + String. The + JaasAuthenticationProvider then creates a + JaasGrantedAuthority (which implements Acegi + Security’s GrantedAuthority interface) containing + both the AuthorityGranter-returned + String and the JAAS principal that the + AuthorityGranter was passed. The + JaasAuthenticationProvider obtains the JAAS + principals by firstly successfully authenticating the user’s + credentials using the JAAS LoginModule, and then + accessing the LoginContext it returns. A call to + LoginContext.getSubject().getPrincipals() is + made, with each resulting principal passed to each + AuthorityGranter defined against the + JaasAuthenticationProvider.setAuthorityGranters(List) + property. + + Acegi Security does not include any production + AuthorityGranters given that every JAAS principal + has an implementation-specific meaning. However, there is a + TestAuthorityGranter in the unit tests that + demonstrates a simple AuthorityGranter + implementation. + + + + + + Siteminder Authentication Mechanism + + + Overview + + Siteminder is a commercial single sign on solution by Computer + Associates. + + Acegi Security provides a filter, + SiteminderAuthenticationProcessingFilter and + provider, SiteminderAuthenticationProvider that can + be used to process requests that have been pre-authenticated by + Siteminder. This filter assumes that you're using Siteminder for + authentication, and that you're using Acegi + Security for authorization. The use of Siteminder + for authorization is not yet directly supported + by Acegi Security. + + When using Siteminder, an agent is setup on your web server to + intercept a principal's first call to your application. The agent + redirects the web request to a single sign-on login page, and once + authenticated, your application receives the request. Inside the HTTP + request is a header - such as SM_USER - which + identifies the authenticated principal (please refer to your + organization's "single sign-on" group for header details in your + particular configuration). + + + + Configuration + + The first step in setting up Acegi Security's Siteminder support + is to define the authentication mechanism that will inspect the HTTP + header discussed earlier. It will be responsible for generating a + UsernamePasswordAuthenticationToken that is later + sent to the SiteminderAuthenticationProvider. Let's + look at an example: + + <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.SiteminderAuthenticationProcessingFilter"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="authenticationFailureUrl"><value>/login.jsp?login_error=1</value></property> + <property name="defaultTargetUrl"><value>/security.do?method=getMainMenu</value></property> + <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property> + <property name="siteminderUsernameHeaderKey"><value>SM_USER</value></property> + <property name="formUsernameParameterKey"><value>j_username</value></property> +</bean> + + In our example above, the bean is being provided an + AuthenticationManager, as is normally needed by + authentication mechanisms. Several URLs are also specified, with the + values being self-explanatory. It's important to also specify the HTTP + header that Acegi Security should inspect. If you additionally want to + support form-based authentication (i.e. in your development + environment where Siteminder is not installed), specify the form's + username parameter as well - just don't do this in production! + + Note that you'll need a + SiteminderAuthenticationProvider + configured against your ProviderManager in order to + use the Siteminder authentication mechanism. Normally an + AuthenticationProvider expects the password + property to match what it retrieves from the + UserDetailsSource, but in this case, authentication + has already been handled by Siteminder, so password property is not + even relevant. This may sound like a security weakness, but remember + that users have to authenticate with Siteminder before your + application ever receives the requests, so the purpose of your custom + UserDetailsService should simply be to build the + complete Authentication object (ie with suitable + GrantedAuthority[]s). + + Advanced tip and word to the wise: If you additionally want to + support form-based authentication in your development environment + (where Siteminder is typically not installed), specify the form's + username parameter as well. Just don't do this in production! + + + + + Run-As Authentication Replacement + + + Overview + + The AbstractSecurityInterceptor is able to + temporarily replace the Authentication object in + the SecurityContext and + SecurityContextHolder during the secure object + callback phase. This only occurs if the original + Authentication object was successfully processed by + the AuthenticationManager and + AccessDecisionManager. The + RunAsManager will indicate the replacement + Authentication object, if any, that should be used + during the SecurityInterceptorCallback. + + By temporarily replacing the Authentication + object during the secure object callback phase, the secured invocation + will be able to call other objects which require different + authentication and authorization credentials. It will also be able to + perform any internal security checks for specific + GrantedAuthority objects. Because Acegi Security + provides a number of helper classes that automatically configure + remoting protocols based on the contents of the + SecurityContextHolder, these run-as replacements + are particularly useful when calling remote web services + + + + Configuration + + A RunAsManager interface is provided by Acegi + Security: + + public Authentication buildRunAs(Authentication authentication, Object object, ConfigAttributeDefinition config); +public boolean supports(ConfigAttribute attribute); +public boolean supports(Class clazz); + + The first method returns the Authentication + object that should replace the existing + Authentication object for the duration of the + method invocation. If the method returns null, it + indicates no replacement should be made. The second method is used by + the AbstractSecurityInterceptor as part of its + startup validation of configuration attributes. The + supports(Class) method is called by a security + interceptor implementation to ensure the configured + RunAsManager supports the type of secure object + that the security interceptor will present. + + One concrete implementation of a RunAsManager + is provided with Acegi Security. The + RunAsManagerImpl class returns a replacement + RunAsUserToken if any + ConfigAttribute starts with + RUN_AS_. If any such + ConfigAttribute is found, the replacement + RunAsUserToken will contain the same principal, + credentials and granted authorities as the original + Authentication object, along with a new + GrantedAuthorityImpl for each + RUN_AS_ ConfigAttribute. Each + new GrantedAuthorityImpl will be prefixed with + ROLE_, followed by the RUN_AS + ConfigAttribute. For example, a + RUN_AS_SERVER will result in the replacement + RunAsUserToken containing a + ROLE_RUN_AS_SERVER granted authority. + + The replacement RunAsUserToken is just like + any other Authentication object. It needs to be + authenticated by the AuthenticationManager, + probably via delegation to a suitable + AuthenticationProvider. The + RunAsImplAuthenticationProvider performs such + authentication. It simply accepts as valid any + RunAsUserToken presented. + + To ensure malicious code does not create a + RunAsUserToken and present it for guaranteed + acceptance by the RunAsImplAuthenticationProvider, + the hash of a key is stored in all generated tokens. The + RunAsManagerImpl and + RunAsImplAuthenticationProvider is created in the + bean context with the same key: + + +<bean id="runAsManager" class="org.acegisecurity.runas.RunAsManagerImpl"> + <property name="key"><value>my_run_as_password</value></property> +</bean> + +<bean id="runAsAuthenticationProvider" class="org.acegisecurity.runas.RunAsImplAuthenticationProvider"> + <property name="key"><value>my_run_as_password</value></property> +</bean> + + + + By using the same key, each RunAsUserToken + can be validated it was created by an approved + RunAsManagerImpl. The + RunAsUserToken is immutable after creation for + security reasons + + + + + Form Authentication Mechanism + + + Overview + + HTTP Form Authentication involves using the + AuthenticationProcessingFilter to process a login + form. This is the most common way that application authenticate end + users. Form-based authentication is entirely compatible with the DAO + and JAAS authentication providers. + + + + Configuration + + The login form simply contains j_username and + j_password input fields, and posts to a URL that is + monitored by the filter (by default + j_acegi_security_check). The filter is defined in + web.xml behind a + FilterToBeanProxy as follows: + + <filter> + <filter-name>Acegi Authentication Processing Filter</filter-name> + <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class> + <init-param> + <param-name>targetClass</param-name> + <param-value>org.acegisecurity.ui.webapp.AuthenticationProcessingFilter</param-value> + </init-param> +</filter> + +<filter-mapping> + <filter-name>Acegi Authentication Processing Filter</filter-name> + <url-pattern>/*</url-pattern> +</filter-mapping> + + For a discussion of FilterToBeanProxy, please + refer to the Filters section. The application context will need to + define the AuthenticationProcessingFilter: + + <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="authenticationFailureUrl"><value>/acegilogin.jsp?login_error=1</value></property> + <property name="defaultTargetUrl"><value>/</value></property> + <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property> +</bean> + + The configured AuthenticationManager + processes each authentication request. If authentication fails, the + browser will be redirected to the + authenticationFailureUrl. The + AuthenticationException will be placed into the + HttpSession attribute indicated by + AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY, + enabling a reason to be provided to the user on the error page. + + If authentication is successful, the resulting + Authentication object will be placed into the + SecurityContextHolder. + + Once the SecurityContextHolder has been + updated, the browser will need to be redirected to the target URL. The + target URL is usually indicated by the HttpSession + attribute specified by + AbstractProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY. + This attribute is automatically set by the + ExceptionTranslationFilter when an + AuthenticationException occurs, so that after login + is completed the user can return to what they were trying to access. + If for some reason the HttpSession does not + indicate the target URL, the browser will be redirected to the + defaultTargetUrl property. + + + + + BASIC Authentication Mechanism + + + Overview + + Acegi Security provides a + BasicProcessingFilter which is capable of + processing basic authentication credentials presented in HTTP headers. + This can be used for authenticating calls made by Spring remoting + protocols (such as Hessian and Burlap), as well as normal user agents + (such as Internet Explorer and Navigator). The standard governing HTTP + Basic Authentication is defined by RFC 1945, Section 11, and the + BasicProcessingFilter conforms with this RFC. Basic + Authentication is an attractive approach to authentication, because it + is very widely deployed in user agents and implementation is extremely + simple (it's just a Base64 encoding of the username:password, + specified in a HTTP header). + + + + Configuration + + To implement HTTP Basic Authentication, it is necessary to + define BasicProcessingFilter in the filter chain. + The application context will need to define the + BasicProcessingFilter and its required + collaborator: + + +<bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="authenticationEntryPoint"><ref bean="authenticationEntryPoint"/></property> +</bean> + +<bean id="authenticationEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint"> + <property name="realmName"><value>Name Of Your Realm</value></property> +</bean> + + + + The configured AuthenticationManager + processes each authentication request. If authentication fails, the + configured AuthenticationEntryPoint will be used to + retry the authentication process. Usually you will use the + BasicProcessingFilterEntryPoint, which returns a + 401 response with a suitable header to retry HTTP Basic + authentication. If authentication is successful, the resulting + Authentication object will be placed into the + SecurityContextHolder. + + If the authentication event was successful, or authentication + was not attempted because the HTTP header did not contain a supported + authentication request, the filter chain will continue as normal. The + only time the filter chain will be interrupted is if authentication + fails and the AuthenticationEntryPoint is called, + as discussed in the previous paragraph + + + + + Digest Authentication + + + Overview + + Acegi Security provides a + DigestProcessingFilter which is capable of + processing digest authentication credentials presented in HTTP + headers. Digest Authentication attempts to solve many of the + weaknesses of Basic authentication, specifically by ensuring + credentials are never sent in clear text across the wire. Many user + agents support Digest Authentication, including FireFox and Internet + Explorer. The standard governing HTTP Digest Authentication is defined + by RFC 2617, which updates an earlier version of the Digest + Authentication standard prescribed by RFC 2069. Most user agents + implement RFC 2617. Acegi Security + DigestProcessingFilter is compatible with the + "auth" quality of protection + (qop) prescribed by RFC 2617, which also provides + backward compatibility with RFC 2069. Digest Authentication is a + highly attractive option if you need to use unencrypted HTTP (ie no + TLS/HTTPS) and wish to maximise security of the authentication + process. Indeed Digest Authentication is a mandatory requirement for + the WebDAV protocol, as noted by RFC 2518 Section 17.1, so we should + expect to see it increasingly deployed and replacing Basic + Authentication. + + Digest Authentication is definitely the most secure choice + between Form Authentication, Basic Authentication and Digest + Authentication, although extra security also means more complex user + agent implementations. Central to Digest Authentication is a "nonce". + This is a value the server generates. Acegi Security's nonce adopts + the following format: + + base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key)) + +expirationTime: The date and time when the nonce expires, expressed in milliseconds +key: A private key to prevent modification of the nonce token + + + The DigestProcessingFilterEntryPoint has a + property specifying the key used for generating the + nonce tokens, along with a nonceValiditySeconds + property for determining the expiration time (default 300, which + equals five minutes). Whist ever the nonce is valid, the digest is + computed by concatenating various strings including the username, + password, nonce, URI being requested, a client-generated nonce (merely + a random value which the user agent generates each request), the realm + name etc, then performing an MD5 hash. Both the server and user agent + perform this digest computation, resulting in different hash codes if + they disagree on an included value (eg password). In Acegi Security + implementation, if the server-generated nonce has merely expired (but + the digest was otherwise valid), the + DigestProcessingFilterEntryPoint will send a + "stale=true" header. This tells the user agent + there is no need to disturb the user (as the password and username etc + is correct), but simply to try again using a new nonce. + + An appropriate value for + DigestProcessingFilterEntryPoint's + nonceValiditySeconds parameter will depend on your + application. Extremely secure applications should note that an + intercepted authentication header can be used to impersonate the + principal until the expirationTime contained in the + nonce is reached. This is the key principle when selecting an + appropriate setting, but it would be unusual for immensely secure + applications to not be running over TLS/HTTPS in the first + instance. + + Because of the more complex implementation of Digest + Authentication, there are often user agent issues. For example, + Internet Explorer fails to present an "opaque" + token on subsequent requests in the same session. Acegi Security + filters therefore encapsulate all state information into the + "nonce" token instead. In our testing, Acegi + Security implementation works reliably with FireFox and Internet + Explorer, correctly handling nonce timeouts etc. + + + + Configuration + + Now that we've reviewed the theory, let's see how to use it. To + implement HTTP Digest Authentication, it is necessary to define + DigestProcessingFilter in the fitler chain. The + application context will need to define the + DigestProcessingFilter and its required + collaborators: + + +<bean id="digestProcessingFilter" class="org.acegisecurity.ui.digestauth.DigestProcessingFilter"> + <property name="userDetailsService"><ref local="jdbcDaoImpl"/></property> + <property name="authenticationEntryPoint"><ref local="digestProcessingFilterEntryPoint"/></property> + <property name="userCache"><ref local="userCache"/></property> +</bean> + +<bean id="digestProcessingFilterEntryPoint" class="org.acegisecurity.ui.digestauth.DigestProcessingFilterEntryPoint"> + <property name="realmName"><value>Contacts Realm via Digest Authentication</value></property> + <property name="key"><value>acegi</value></property> + <property name="nonceValiditySeconds"><value>10</value></property> +</bean> + + + + The configured UserDetailsService is needed + because DigestProcessingFilter must have direct + access to the clear text password of a user. Digest Authentication + will NOT work if you are using encoded passwords in your DAO. The DAO + collaborator, along with the UserCache, are + typically shared directly with a + DaoAuthenticationProvider. The + authenticationEntryPoint property must be + DigestProcessingFilterEntryPoint, so that + DigestProcessingFilter can obtain the correct + realmName and key for digest + calculations. + + Like BasicAuthenticationFilter, if + authentication is successful an Authentication + request token will be placed into the + SecurityContextHolder. If the authentication event + was successful, or authentication was not attempted because the HTTP + header did not contain a Digest Authentication request, the filter + chain will continue as normal. The only time the filter chain will be + interrupted is if authentication fails and the + AuthenticationEntryPoint is called, as discussed in + the previous paragraph. + + Digest Authentication's RFC offers a range of additional + features to further increase security. For example, the nonce can be + changed on every request. Despite this, Acegi Security implementation + was designed to minimise the complexity of the implementation (and the + doubtless user agent incompatibilities that would emerge), and avoid + needing to store server-side state. You are invited to review RFC 2617 + if you wish to explore these features in more detail. As far as we are + aware, Acegi Security implementation does comply with the minimum + standards of this RFC. + + + + + Anonymous Authentication + + + Overview + + Particularly in the case of web request URI security, sometimes + it is more convenient to assign configuration attributes against every + possible secure object invocation. Put differently, sometimes it is + nice to say ROLE_SOMETHING is required by default + and only allow certain exceptions to this rule, such as for login, + logout and home pages of an application. There are also other + situations where anonymous authentication would be desired, such as + when an auditing interceptor queries the + SecurityContextHolder to identify which principal + was responsible for a given operation. Such classes can be authored + with more robustness if they know the + SecurityContextHolder always contains an + Authentication object, and never + null. + + + + Configuration + + Acegi Security provides three classes that together provide an + anonymous authentication feature. + AnonymousAuthenticationToken is an implementation + of Authentication, and stores the + GrantedAuthority[]s which apply to the anonymous + principal. There is a corresponding + AnonymousAuthenticationProvider, which is chained + into the ProviderManager so that + AnonymousAuthenticationTokens are accepted. + Finally, there is an AnonymousProcessingFilter, which is chained after + the normal authentication mechanisms and automatically add an + AnonymousAuthenticationToken to the + SecurityContextHolder if there is no existing + Authentication held there. The definition of the + filter and authentication provider appears as follows: + + +<bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter"> + <property name="key"><value>foobar</value></property> + <property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property> +</bean> + +<bean id="anonymousAuthenticationProvider" class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> + <property name="key"><value>foobar</value></property> +</bean> + + + + The key is shared between the filter and + authentication provider, so that tokens created by the former are + accepted by the latter. The userAttribute is + expressed in the form of + usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]. + This is the same syntax as used after the equals sign for + InMemoryDaoImpl's userMap + property. + + As explained earlier, the benefit of anonymous authentication is + that all URI patterns can have security applied to them. For + example: + + +<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property> + <property name="objectDefinitionSource"> + <value> + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT + /index.jsp=ROLE_ANONYMOUS,ROLE_USER + /hello.htm=ROLE_ANONYMOUS,ROLE_USER + /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER + /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER + /**=ROLE_USER + </value> + </property> +</bean> + + Rounding out the anonymous authentication discussion + is the AuthenticationTrustResolver interface, with + its corresponding AuthenticationTrustResolverImpl + implementation. This interface provides an + isAnonymous(Authentication) method, which allows + interested classes to take into account this special type of + authentication status. The + ExceptionTranslationFilter uses this interface in + processing AccessDeniedExceptions. If an + AccessDeniedException is thrown, and the + authentication is of an anonymous type, instead of throwing a 403 + (forbidden) response, the filter will instead commence the + AuthenticationEntryPoint so the principal can + authenticate properly. This is a necessary distinction, otherwise + principals would always be deemed "authenticated" and never be given + an opportunity to login via form, basic, digest or some other normal + authentication mechanism + + + + + Remember-Me Authentication + + + Overview + + Remember-me authentication refers to web sites being able to + remember the identity of a principal between sessions. This is + typically accomplished by sending a cookie to the browser, with the + cookie being detected during future sessions and causing automated + login to take place. Acegi Security provides the necessary hooks so + that such operations can take place, along with providing a concrete + implementation that uses hashing to preserve the security of + cookie-based tokens. + + + + Configuration + + Remember-me authentication is not used with basic + authentication, given it is often not used with + HttpSessions. Remember-me is used with + AuthenticationProcessingFilter, and is implemented + via hooks in the AbstractProcessingFilter + superclass. The hooks will invoke a concrete + RememberMeServices at the appropriate times. The + interface looks like this: + + public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response); +public void loginFail(HttpServletRequest request, HttpServletResponse response); +public void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication); + + Please refer to JavaDocs for a fuller discussion on what the + methods do, although note at this stage + AbstractProcessingFilter only calls the + loginFail() and loginSuccess() + methods. The autoLogin() method is called by + RememberMeProcessingFilter whenever the + SecurityContextHolder does not contain an + Authentication. This interface therefore provides + the underlaying remember-me implementation with sufficient + notification of authentication-related events, and delegates to the + implementation whenever a candidate web request might contain a cookie + and wish to be remembered. + + This design allows any number of remember-me implementation + strategies. In the interests of simplicity and avoiding the need for + DAO implementations that specify write and create methods, Acegi + Security's only concrete implementation, + TokenBasedRememberMeServices, uses hashing to + achieve a useful remember-me strategy. In essence a cookie is sent to + the browser upon successful interactive authentication, with that + cookie being composed as follows: + + base64(username + ":" + expirationTime + ":" + md5Hex(username + ":" + expirationTime + ":" password + ":" + key)) + +username: As identifiable to TokenBasedRememberMeServices.getUserDetailsService() +password: That matches the relevant UserDetails retrieved from TokenBasedRememberMeServices.getUserDetailsService() +expirationTime: The date and time when the remember-me token expires, expressed in milliseconds +key: A private key to prevent modification of the remember-me token + + + As such the remember-me token is valid only for the period + specified, and provided that the username, password and key does not + change. Notably, this has a potential security issue in that a + captured remember-me token will be usable from any user agent until + such time as the token expires. This is the same issue as with digest + authentication. If a principal is aware a token has been captured, + they can easily change their password and immediately invalidate all + remember-me tokens on issue. However, if more significant security is + needed a rolling token approach should be used (this would require a + database) or remember-me services should simply not be used. + + TokenBasedRememberMeServices generates a + RememberMeAuthenticationToken, which is processed + by RememberMeAuthenticationProvider. A + key is shared between this authentication provider + and the TokenBasedRememberMeServices. In addition, + TokenBasedRememberMeServices requires A + UserDetailsService from which it can retrieve the username and + password for signature comparison purposes, and generate the + RememberMeAuthenticationToken to contain the + correct GrantedAuthority[]s. Some sort of logout + command should be provided by the application (typically via a JSP) + that invalidates the cookie upon user request. See the Contacts Sample + application's logout.jsp for an example. + + The beans required in an application context to enable + remember-me services are as follows: + + +<bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"> + <property name="rememberMeServices"><ref local="rememberMeServices"/></property> +</bean> + +<bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"> + <property name="userDetailsService"><ref local="jdbcDaoImpl"/></property> + <property name="key"><value>springRocks</value></property> +</bean> + +<bean id="rememberMeAuthenticationProvider" class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> + <property name="key"><value>springRocks</value></property> +</bean> + + Don't forget to add your + RememberMeServices implementation to your + AuthenticationProcessingFilter.setRememberMeServices() + property, include the + RememberMeAuthenticationProvider in your + AuthenticationManager.setProviders() list, and add + a call to RememberMeProcessingFilter into your + FilterChainProxy (typically immediately after your + AuthenticationProcessingFilter) + + + + + X509 Authentication + + + Overview + + The most common use of X509 certificate authentication is in + verifying the identity of a server when using SSL, most commonly when + using HTTPS from a browser. The browser will automatically check that + the certificate presented by a server has been issued (ie digitally + signed) by one of a list of trusted certificate authorities which it + maintains. + + You can also use SSL with mutual authentication; + the server will then request a valid certificate from the client as + part of the SSL handshake. The server will authenticate the client by + checking that it's certificate is signed by an acceptable authority. + If a valid certificate has been provided, it can be obtained through + the servlet API in an application. Acegi Security X509 module extracts + the certificate using a filter and passes it to the configured X509 + authentication provider to allow any additional application-specific + checks to be applied. It also maps the certificate to an application + user and loads that user's set of granted authorities for use with the + standard Acegi Security infrastructure. + + You should be familiar with using certificates and setting up + client authentication for your servlet container before attempting to + use it with Acegi Security. Most of the work is in creating and + installing suitable certificates and keys. For example, if you're + using Tomcat then read the instructions here . + It's important that you get this working before trying it out with + Acegi Security + + + + Using X509 with Acegi Security + + With X509 authentication, there is no explicit login procedure + so the implementation is relatively simple; there is no need to + redirect requests in order to interact with the user. As a result, + some of the classes behave slightly differently from their equivalents + in other packages. For example, the default entry point + class, which is normally responsible for starting the authentication + process, is only invoked if the certificate is rejected and it always + returns an error to the user. With a suitable bean configuration, the + normal sequence of events is as follows + + The X509ProcessingFilter extracts + the certificate from the request and uses it as the credentials + for an authentication request. The generated authentication + request is an X509AuthenticationToken. + The request is passed to the authentication manager. + + + + The X509AuthenticationProvider + receives the token. Its main concern is to obtain the user + information (in particular the user's granted authorities) that + matches the certificate. It delegates this responsibility to an + X509AuthoritiesPopulator. + + + + The populator's single method, + getUserDetails(X509Certificate + userCertificate) is invoked. Implementations should + return a UserDetails instance containing + the array of GrantedAuthority objects for + the user. This method can also choose to reject the certificate + (for example if it doesn't contain a matching user name). In + such cases it should throw a + BadCredentialsException. A + DAO-based implementation, + DaoX509AuthoritiesPopulator, is provided + which extracts the user's name from the subject common + name (CN) in the certificate. It also allows you to set + your own regular expression to match a different part of the + subject's distinguished name. A UserDetailsService is used to + load the user information. + + + + If everything has gone smoothly then there should be a + valid Authentication object in the secure + context and the invocation will procede as normal. If no + certificate was found, or the certificate was rejected, then the + ExceptionTranslationFilter will invoke + the X509ProcessingFilterEntryPoint which + returns a 403 error (forbidden) to the user. + + + + + + Configuration + + There is a version of the Contacts Sample Application which + uses X509. Copy the beans and filter setup from this as a starting + point for configuring your own application. A set of example + certificates is also included which you can use to configure your + server. These are + + marissa.p12: A PKCS12 format file + containing the client key and certificate. These should be + installed in your browser. It maps to the user + marissa in the application. + + + + server.p12: The server certificate + and key for HTTPS connections. + + + + ca.jks: A Java keystore containing + the certificate for the authority which issued marissa's + certificate. This will be used by the container to validate + client certificates. + + For JBoss 3.2.7 (with Tomcat 5.0), the SSL + configuration in the server.xml file looks like + this +<!-- SSL/TLS Connector configuration --> +<Connector port="8443" address="${jboss.bind.address}" + maxThreads="100" minSpareThreads="5" maxSpareThreads="15" + scheme="https" secure="true" + sslProtocol = "TLS" + clientAuth="true" keystoreFile="${jboss.server.home.dir}/conf/server.p12" + keystoreType="PKCS12" keystorePass="password" + truststoreFile="${jboss.server.home.dir}/conf/ca.jks" + truststoreType="JKS" truststorePass="password" +/> + + clientAuth can also be set to + want if you still want SSL connections to + succeed even if the client doesn't provide a certificate. Obviously + these clients won't be able to access any objects secured by Acegi + Security (unless you use a non-X509 authentication mechanism, such as + BASIC authentication, to authenticate the user) + + + + + LDAP Authentication + + + Overview + + LDAP is often used by organizations as a central repository for + user information and as an authentication service. It can also be used + to store the role information for application users. + + There are many different scenarios for how an LDAP server may be + configured so Acegi LDAP provider is fully configurable. It uses + separate strategy interfaces for authentication and role retrieval and + provides default implementations which can be configured to handle a + wide range of situations. + + You should be familiar with LDAP before trying to use it with + Acegi. The following link provides a good introduction to the concepts + involved and a guide to setting up a directory using the free LDAP + server OpenLDAP: . Some familiarity + with the JNDI APIs used to access LDAP from Java may also be useful. + We don't use any third-party LDAP libraries (Mozilla/Netscape, JLDAP + etc.) in the LDAP provider. + + + + Using LDAP with Acegi Security + + The main LDAP provider class is + org.acegisecurity.providers.ldap.LdapAuthenticationProvider. + This bean doesn't actually do much itself other than implement the + retrieveUser method required by its base + class, + AbstractUserDetailsAuthenticationProvider. It + delegates the work to two other beans, an + LdapAuthenticator and an + LdapAuthoritiesPopulator which are + responsible for authenticating the user and retrieving the user's set + of GrantedAuthoritys + respectively. + + + LdapAuthenticator Implementations + + The authenticator is also responsible for retrieving any + required user attributes. This is because the permissions on the + attributes may depend on the type of authentication being used. For + example, if binding as the user, it may be necessary to read them + with the user's own permissions. + + There are currently two authentication strategies supplied + with Acegi Security: + + Authentication directly to the LDAP server ("bind" + authentication). + + + + Password comparison, where the password supplied by the + user is compared with the one stored in the repository. This + can either be done by retrieving the value of the password + attribute and checking it locally or by performing an LDAP + "compare" operation, where the supplied password is passed to + the server for comparison and the real password value is never + retrieved. + + + + + Common Functionality + + Before it is possible to authenticate a user (by either + strategy), the distinguished name (DN) has to be obtained from the + login name supplied to the application. This can be done either by + simple pattern-matching (by setting the + setUserDnPatterns array property) or by + setting the userSearch property. For the DN + pattern-matching approach, a standard Java pattern format is used, + and the login name will be substituted for the parameter + {0}. The pattern should be relative to the + DN that the configured + InitialDirContextFactory will bind + to (see the section on connecting to the LDAP + server for more information on this). For example, if you + are using an LDAP server specified by the URL + ldap://monkeymachine.co.uk/dc=acegisecurity,dc=org, + and have a pattern uid={0},ou=greatapes, then a + login name of "gorilla" will map to a DN + uid=gorilla,ou=greatapes,dc=acegisecurity,dc=org. + Each configured DN pattern will be tried in turn until a match is + found. For information on using a search, see the section on search objects below. A + combination of the two approaches can also be used - the patterns + will be checked first and if no matching DN is found, the search + will be used. + + + + BindAuthenticator + + The class + org.acegisecurity.providers.ldap.authenticator.BindAuthenticator + implements the bind authentication strategy. It simply attempts to + bind as the user. + + + + PasswordComparisonAuthenticator + + The class + org.acegisecurity.providers.ldap.authenticator.PasswordComparisonAuthenticator + implements the password comparison authentication strategy. + + + + Active Directory Authentication + + In addition to standard LDAP authentication (binding with a + DN), Active Directory has its own non-standard syntax for user + authentication. + + + + + Connecting to the LDAP Server + + The beans discussed above have to be able to connect to the + server. They both have to be supplied with an + InitialDirContextFactory instance. + Unless you have special requirements, this will usually be a + DefaultInitialDirContextFactory bean, which + can be configured with the URL of your LDAP server and optionally + with the username and password of a "manager" user which will be + used by default when binding to the server (instead of binding + anonymously). It currently supports "simple" LDAP + authentication. + + DefaultInitialDirContextFactory uses + Sun's JNDI LDAP implementation by default (the one that comes with + the JDK). It also supports the built in connection pooling offered + by Sun's provider. Connections which are obtained either anonymously + or with the "manager" user's identity will be pooled automatically. + Connections obtained with a specific user's identity will not be + pooled. Connection pooling can be disabled completely by setting the + useConnectionPool property to false. + + See the class + Javadoc and source for more information on this bean and its + properties. + + + + LDAP Search Objects + + Often more a more complicated strategy than simple DN-matching + is required to locate a user entry in the directory. This can be + encapsulated in an LdapUserSearch + instance which can be supplied to the authenticator implementations, + for example, to allow them to locate a user. The supplied + implementation is + FilterBasedLdapUserSearch. + + + <classname>FilterBasedLdapUserSearch</classname> + + This bean uses an LDAP filter to match the user object in + the directory. The process is explained in the Javadoc for the + corresponding search method on the JDK + DirContext class. As explained there, the search filter + can be supplied with parameters. For this class, the only valid + parameter is {0} which will be replaced + with the user's login name. + + + + + + Configuration + + There is a version of the Contacts Sample Application which + uses LDAP. You can copy the beans and filter setup from this as a + starting point for configuring your own application. + + A typical configuration, using some of the beans we've discussed + above, might look like this: + <bean id="initialDirContextFactory" + class="org.acegisecurity.ldap.DefaultInitialDirContextFactory"> + <constructor-arg value="ldap://monkeymachine:389/dc=acegisecurity,dc=org"/> + <property name="managerDn"><value>cn=manager,dc=acegisecurity,dc=org</value></property> + <property name="managerPassword"><value>password</value></property> + </bean> + + <bean id="userSearch" + class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch"> + <constructor-arg index="0"> + <value></value> + </constructor-arg> + <constructor-arg index="1"> + <value>(uid={0})</value> + </constructor-arg> + <constructor-arg index="2"> + <ref local="initialDirContextFactory" /> + </constructor-arg> + <property name="searchSubtree"> + <value>true</value> + </property> + </bean> + + <bean id="ldapAuthProvider" + class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider"> + <constructor-arg> + <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator"> + <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg> + <property name="userDnPatterns"><list><value>uid={0},ou=people</value></list></property> + </bean> + </constructor-arg> + <constructor-arg> + <bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator"> + <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg> + <constructor-arg><value>ou=groups</value></constructor-arg> + <property name="groupRoleAttribute"><value>ou</value></property> + </bean> + </constructor-arg> + </bean> + + This would set up the provider to access an LDAP + server with URL + ldap://monkeymachine:389/dc=acegisecurity,dc=org. + Authentication will be performed by attempting to bind with the DN + uid=<user-login-name>,ou=people,dc=acegisecurity,dc=org. + After successful authentication, roles will be assigned to the user by + searching under the DN + ou=groups,dc=acegisecurity,dc=org with the default + filter (member=<user's-DN>). The role name + will be taken from the ou attribute of each + match. + + We've also included the configuration for a user search object, + which uses the filter + (uid=<user-login-name>). This could be used + instead of the DN-pattern (or in addition to it), by setting the + authenticator's userSearch property. The + authenticator would then call the search object to obtain the correct + user's DN before attempting to bind as this user. + + + + + CAS Authentication + + + Overview + + JA-SIG produces an enterprise-wide single sign on system known + as CAS. Unlike other initiatives, JA-SIG's Central Authentication + Service is open source, widely used, simple to understand, platform + independent, and supports proxy capabilities. Acegi Security fully + supports CAS, and provides an easy migration path from + single-application deployments of Acegi Security through to + multiple-application deployments secured by an enterprise-wide CAS + server. + + You can learn more about CAS at + http://www.ja-sig.org/products/cas/. You will need + to visit this URL to download the CAS Server files. Whilst Acegi + Security includes two CAS libraries in the "-with-dependencies" ZIP + file, you will still need the CAS Java Server Pages and + web.xml to customise and deploy your CAS + server. + + + + How CAS Works + + Whilst the CAS web site above contains two documents that detail + the architecture of CAS, we present the general overview again here + within the context of Acegi Security. The following refers to both CAS + 2.0 (produced by Yale) and CAS 3.0 (produced by JA-SIG), being the + versions of CAS that Acegi Security supports. + + Somewhere in your enterprise you will need to setup a CAS + server. The CAS server is simply a standard WAR file, so there isn't + anything difficult about setting up your server. Inside the WAR file + you will customise the login and other single sign on pages displayed + to users. + + If you are deploying CAS 2.0, you will also need to specify in + the web.xml a PasswordHandler. The + PasswordHandler has a simple method that returns a + boolean as to whether a given username and password is valid. Your + PasswordHandler implementation will need to link + into some type of backend authentication repository, such as an LDAP + server or database. + + If you are already running an existing CAS 2.0 server instance, + you will have already established a + PasswordHandler. If you do not already have a + PasswordHandler, you might prefer to use Acegi + Security CasPasswordHandler class. This class + delegates through to the standard Acegi Security + AuthenticationManager, enabling you to use a + security configuration you might already have in place. You do not + need to use the CasPasswordHandler class on your + CAS server if you do not wish. Acegi Security will function as a CAS + client successfully irrespective of the + PasswordHandler you've chosen for your CAS + server. + + If you are deploying CAS 3.0, you will also need to specify an + AuthenticationHandler in the + deployerConfigContext.xml included with CAS. The + AuthenticationHandler has a simple method that + returns a boolean as to whether a given set of Credentials is valid. + Your AuthenticationHandler implementation will need + to link into some type of backend authentication repository, such as + an LDAP server or database. CAS itself includes numerous + AuthenticationHandlers out of the box to assist + with this. + + If you are already running an existing CAS 3.0 server instance, + you will have already established an + AuthenticationHandler. If you do not already have + an AuthenticationHandler, you might prefer to use + Acegi Security CasAuthenticationHandler class. This + class delegates through to the standard Acegi Security + AuthenticationManager, enabling you to use a + security configuration you might already have in place. You do not + need to use the CasAuthenticationHandler class on + your CAS server if you do not wish. Acegi Security will function as a + CAS client successfully irrespective of the + AuthenticationHandler you've chosen for your CAS + server. + + Apart from the CAS server itself, the other key player is of + course the secure web applications deployed throughout your + enterprise. These web applications are known as "services". There are + two types of services: standard services and proxy services. A proxy + service is able to request resources from other services on behalf of + the user. This will be explained more fully later. + + Services can be developed in a large variety of languages, due + to CAS 2.0's very light XML-based protocol. The JA-SIG CAS home page + contains a clients archive which demonstrates CAS clients in Java, + Active Server Pages, Perl, Python and others. Naturally, Java support + is very strong given the CAS server is written in Java. You do not + need to use any of CAS' client classes in applications secured by + Acegi Security. This is handled transparently for you. + + The basic interaction between a web browser, CAS server and an + Acegi Security for System Spring secured service is as follows: + + + + The web user is browsing the service's public pages. CAS or + Acegi Security is not involved. + + + + The user eventually requests a page that is either secure or + one of the beans it uses is secure. Acegi Security's + ExceptionTranslationFilter will detect the + AuthenticationException. + + + + Because the user's Authentication object + (or lack thereof) caused an + AuthenticationException, the + ExceptionTranslationFilter will call the + configured AuthenticationEntryPoint. If using + CAS, this will be the + CasProcessingFilterEntryPoint class. + + + + The CasProcessingFilterEntry point will + redirect the user's browser to the CAS server. It will also + indicate a service parameter, which is the + callback URL for Acegi Security service. For example, the URL to + which the browser is redirected might be + https://my.company.com/cas/login?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Fj_acegi_cas_security_check. + + + + After the user's browser redirects to CAS, they will be + prompted for their username and password. If the user presents a + session cookie which indicates they've previously logged on, they + will not be prompted to login again (there is an exception to this + procedure, which we'll cover later). CAS will use the + PasswordHandler (or + AuthenticationHandler if using CAS 3.0) + discussed above to decide whether the username and password is + valid. + + + + Upon successful login, CAS will redirect the user's browser + back to the original service. It will also include a + ticket parameter, which is an opaque string + representing the "service ticket". Continuing our earlier example, + the URL the browser is redirected to might be + https://server3.company.com/webapp/j_acegi_cas_security_check?ticket=ST-0-ER94xMJmn6pha35CQRoZ. + + + + Back in the service web application, the + CasProcessingFilter is always listening for + requests to /j_acegi_cas_security_check (this + is configurable, but we'll use the defaults in this introduction). + The processing filter will construct a + UsernamePasswordAuthenticationToken + representing the service ticket. The principal will be equal to + CasProcessingFilter.CAS_STATEFUL_IDENTIFIER, + whilst the credentials will be the service ticket opaque value. + This authentication request will then be handed to the configured + AuthenticationManager. + + + + The AuthenticationManager implementation + will be the ProviderManager, which is in turn + configured with the CasAuthenticationProvider. + The CasAuthenticationProvider only responds to + UsernamePasswordAuthenticationTokens containing + the CAS-specific principal (such as + CasProcessingFilter.CAS_STATEFUL_IDENTIFIER) + and CasAuthenticationTokens (discussed + later). + + + + CasAuthenticationProvider will validate + the service ticket using a TicketValidator + implementation. Acegi Security includes one implementation, the + CasProxyTicketValidator. This implementation a + ticket validation class included in the CAS client library. The + CasProxyTicketValidator makes a HTTPS request + to the CAS server in order to validate the service ticket. The + CasProxyTicketValidator may also include a + proxy callback URL, which is included in this example: + https://my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Fj_acegi_cas_security_check&ticket=ST-0-ER94xMJmn6pha35CQRoZ&pgtUrl=https://server3.company.com/webapp/casProxy/receptor. + + + + Back on the CAS server, the proxy validation request will be + received. If the presented service ticket matches the service URL + the ticket was issued to, CAS will provide an affirmative response + in XML indicating the username. If any proxy was involved in the + authentication (discussed below), the list of proxies is also + included in the XML response. + + + + [OPTIONAL] If the request to the CAS validation service + included the proxy callback URL (in the pgtUrl + parameter), CAS will include a pgtIou string in + the XML response. This pgtIou represents a + proxy-granting ticket IOU. The CAS server will then create its own + HTTPS connection back to the pgtUrl. This is to + mutually authenticate the CAS server and the claimed service URL. + The HTTPS connection will be used to send a proxy granting ticket + to the original web application. For example, + https://server3.company.com/webapp/casProxy/receptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH. + We suggest you use CAS' ProxyTicketReceptor + servlet to receive these proxy-granting tickets, if they are + required. + + + + The CasProxyTicketValidator will parse + the XML received from the CAS server. It will return to the + CasAuthenticationProvider a + TicketResponse, which includes the username + (mandatory), proxy list (if any were involved), and proxy-granting + ticket IOU (if the proxy callback was requested). + + + + Next CasAuthenticationProvider will call + a configured CasProxyDecider. The + CasProxyDecider indicates whether the proxy + list in the TicketResponse is acceptable to the + service. Several implementations are provided with Acegi Security + System: RejectProxyTickets, + AcceptAnyCasProxy and + NamedCasProxyDecider. These names are largely + self-explanatory, except NamedCasProxyDecider + which allows a List of trusted proxies to be + provided. + + + + CasAuthenticationProvider will next + request a CasAuthoritiesPopulator to advise the + GrantedAuthority objects that apply to the user + contained in the TicketResponse. Acegi Security + includes a DaoCasAuthoritiesPopulator which + simply uses the UserDetailsService + infrastructure to find the UserDetails and + their associated GrantedAuthoritys. Note that + the password and enabled/disabled status of + UserDetails returned by the + UserDetailsService are ignored, as the CAS + server is responsible for authentication decisions. + DaoCasAuthoritiesPopulator is only concerned + with retrieving the GrantedAuthoritys. + + + + If there were no problems, + CasAuthenticationProvider constructs a + CasAuthenticationToken including the details + contained in the TicketResponse and the + GrantedAuthoritys. The + CasAuthenticationToken contains the hash of a + key, so that the CasAuthenticationProvider + knows it created it. + + + + Control then returns to + CasProcessingFilter, which places the created + CasAuthenticationToken into the + HttpSession attribute named + HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY. + + + + The user's browser is redirected to the original page that + caused the AuthenticationException. + + + + As the Authentication object is now in + the well-known location, it is handled like any other + authentication approach. Usually the + HttpSessionIntegrationFilter will be used to + associate the Authentication object with the + SecurityContextHolder for the duration of each + request. + + + + It's good that you're still here! It might sound involved, but + you can relax as Acegi Security classes hide much of the complexity. + Let's now look at how this is configured + + + + Optional CAS Server Setup + + Acegi Security can even act as the backend which a CAS version + 2.0 or 3.0 server utilises. The configuration approach is described + below. Of course, if you have an existing CAS environment you might + just like to use it instead. + + + CAS Version 2.0 + + As mentioned above, Acegi Security includes a + PasswordHandler that bridges your existing + AuthenticationManager into CAS 2.0. You do not + need to use this PasswordHandler to use Acegi + Security on the client side (any CAS + PasswordHandler will do). + + To install, you will need to download and extract the CAS + server archive. We used version 2.0.12. There will be a + /web directory in the root of the deployment. + Copy an applicationContext.xml containing your + AuthenticationManager as well as the + CasPasswordHandler into the + /web/WEB-INF directory. A sample + applicationContext.xml is included below: + + +<bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> + <property name="userMap"> + <value> + marissa=koala,ROLES_IGNORED_BY_CAS + dianne=emu,ROLES_IGNORED_BY_CAS + scott=wombat,ROLES_IGNORED_BY_CAS + peter=opal,disabled,ROLES_IGNORED_BY_CAS + </value> + </property> +</bean> + +<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> + <property name="userDetailsService"><ref bean="inMemoryDaoImpl"/></property> +</bean> + +<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> + <property name="providers"> + <list> + <ref bean="daoAuthenticationProvider"/> + </list> + </property> +</bean> + +<bean id="casPasswordHandler" class="org.acegisecurity.adapters.cas.CasPasswordHandler"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> +</bean> + + + + Note the granted authorities are ignored by CAS because it has + no way of communicating the granted authorities to calling + applications. CAS is only concerned with username and passwords (and + the enabled/disabled status). + + Next you will need to edit the existing + /web/WEB-INF/web.xml file. Add (or edit in the + case of the authHandler property) the following + lines: + + + +<context-param> + <param-name>edu.yale.its.tp.cas.authHandler</param-name> + <param-value>org.acegisecurity.adapters.cas.CasPasswordHandlerProxy</param-value> +</context-param> + +<context-param> + <param-name>contextConfigLocation</param-name> + <param-value>/WEB-INF/applicationContext.xml</param-value> +</context-param> + +<listener> + <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> +</listener> + + + + Copy the spring.jar and + acegi-security.jar files into + /web/WEB-INF/lib. Now use the ant + dist task in the build.xml in the root + of the directory structure. This will create + /lib/cas.war, which is ready for deployment to + your servlet container. + + Note CAS heavily relies on HTTPS. You can't even test the + system without a HTTPS certificate. Whilst you should refer to your + web container's documentation on setting up HTTPS, if you need some + additional help or a test certificate you might like to check the + samples/contacts/etc/ssl directory + + + + CAS Version 3.0 + + As mentioned above, Acegi Security includes an + AuthenticationHandler that bridges your existing + AuthenticationManager into CAS 3.0. You do not + need to use this AuthenticationHandler to use + Acegi Security on the client side (any CAS + AuthenticationHandler will do). + + To install, you will need to download and extract the CAS + server archive. We used version 3.0.4. There will be a + /webapp directory in the root of the deployment. + Edit the an deployerConfigContext.xml so that it + contains your AuthenticationManager as well as + the CasAuthenticationHandler. A sample + applicationContext.xml is included below: + + + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> + <beans> + <bean + id="authenticationManager" + class="org.jasig.cas.authentication.AuthenticationManagerImpl"> + <property name="credentialsToPrincipalResolvers"> + <list> + <bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" /> + <bean class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" /> + </list> + </property> + + <property name="authenticationHandlers"> + <list> + <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" /> + <bean class="org.acegisecurity.adapters.cas3.CasAuthenticationHandler"> + <property name="authenticationManager" ref="acegiAuthenticationManager" /> + </bean> + </list> + </property> + </bean> + + + <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> + <property name="userMap"> + <value> + marissa=koala,ROLES_IGNORED_BY_CAS + dianne=emu,ROLES_IGNORED_BY_CAS + scott=wombat,ROLES_IGNORED_BY_CAS + peter=opal,disabled,ROLES_IGNORED_BY_CAS + </value> + </property> + </bean> + + <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> + <property name="userDetailsService"><ref bean="inMemoryDaoImpl"/></property> + </bean> + + <bean id="acegiAuthenticationManager" class="org.acegisecurity.providers.ProviderManager"> + <property name="providers"> + <list> + <ref bean="daoAuthenticationProvider"/> + </list> + </property> + </bean> + </beans> + + + + Note the granted authorities are ignored by CAS because it has + no way of communicating the granted authorities to calling + applications. CAS is only concerned with username and passwords (and + the enabled/disabled status). + + Copy acegi-security.jar and + acegi-security-cas.jar files into + /localPlugins/lib. Now use the ant + war task in the build.xml in the + /localPlugins directory. This will create + /localPlugins/target/cas.war, which is ready for + deployment to your servlet container. + + Note CAS heavily relies on HTTPS. You can't even test the + system without a HTTPS certificate. Whilst you should refer to your + web container's documentation on setting up HTTPS, if you need some + additional help or a test certificate you might like to check the + CAS documentation on setting up SSL: + http://www.ja-sig.org/products/cas/server/ssl/index.html + + + + + Configuration of CAS Client + + The web application side of CAS is made easy due to Acegi + Security. It is assumed you already know the basics of using Acegi + Security, so these are not covered again below. Only the CAS-specific + beans are mentioned. + + You will need to add a ServiceProperties bean + to your application context. This represents your service: + + + +<bean id="serviceProperties" class="org.acegisecurity.ui.cas.ServiceProperties"> + <property name="service"><value>https://localhost:8443/contacts-cas/j_acegi_cas_security_check</value></property> + <property name="sendRenew"><value>false</value></property> +</bean> + + + + The service must equal a URL that will be + monitored by the CasProcessingFilter. The + sendRenew defaults to false, but should be set to + true if your application is particularly sensitive. What this + parameter does is tell the CAS login service that a single sign on + login is unacceptable. Instead, the user will need to re-enter their + username and password in order to gain access to the service. + + The following beans should be configured to commence the CAS + authentication process: + + +<bean id="casProcessingFilter" class="org.acegisecurity.ui.cas.CasProcessingFilter"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="authenticationFailureUrl"><value>/casfailed.jsp</value></property> + <property name="defaultTargetUrl"><value>/</value></property> + <property name="filterProcessesUrl"><value>/j_acegi_cas_security_check</value></property> +</bean> + +<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> + <property name="authenticationEntryPoint"><ref local="casProcessingFilterEntryPoint"/></property> +</bean> + +<bean id="casProcessingFilterEntryPoint" class="org.acegisecurity.ui.cas.CasProcessingFilterEntryPoint"> + <property name="loginUrl"><value>https://localhost:8443/cas/login</value></property> + <property name="serviceProperties"><ref bean="serviceProperties"/></property> +</bean> + + + + You will also need to add the + CasProcessingFilter to web.xml: + + +<filter> + <filter-name>Acegi CAS Processing Filter</filter-name> + <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class> + <init-param> + <param-name>targetClass</param-name> + <param-value>org.acegisecurity.ui.cas.CasProcessingFilter</param-value> + </init-param> +</filter> + +<filter-mapping> + <filter-name>Acegi CAS Processing Filter</filter-name> + <url-pattern>/*</url-pattern> +</filter-mapping> + + + + The CasProcessingFilter has very similar + properties to the AuthenticationProcessingFilter + (used for form-based logins). Each property is + self-explanatory. + + For CAS to operate, the + ExceptionTranslationFilter must have its + authenticationEntryPoint property set to the + CasProcessingFilterEntryPoint bean. + + The CasProcessingFilterEntryPoint must refer + to the ServiceProperties bean (discussed above), + which provides the URL to the enterprise's CAS login server. This is + where the user's browser will be redirected. + + Next you need to add an AuthenticationManager + that uses CasAuthenticationProvider and its + collaborators: + + +<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> + <property name="providers"> + <list> + <ref bean="casAuthenticationProvider"/> + </list> + </property> +</bean> + +<bean id="casAuthenticationProvider" class="org.acegisecurity.providers.cas.CasAuthenticationProvider"> + <property name="casAuthoritiesPopulator"><ref bean="casAuthoritiesPopulator"/></property> + <property name="casProxyDecider"><ref bean="casProxyDecider"/></property> + <property name="ticketValidator"><ref bean="casProxyTicketValidator"/></property> + <property name="statelessTicketCache"><ref bean="statelessTicketCache"/></property> + <property name="key"><value>my_password_for_this_auth_provider_only</value></property> +</bean> + +<bean id="casProxyTicketValidator" class="org.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator"> + <property name="casValidate"><value>https://localhost:8443/cas/proxyValidate</value></property> + <property name="proxyCallbackUrl"><value>https://localhost:8443/contacts-cas/casProxy/receptor</value></property> + <property name="serviceProperties"><ref bean="serviceProperties"/></property> + <!-- <property name="trustStore"><value>/some/path/to/your/lib/security/cacerts</value></property> --> +</bean> + +<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> + <property name="configLocation"> + <value>classpath:/ehcache-failsafe.xml</value> + </property> +</bean> + +<bean id="ticketCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> + <property name="cacheManager"> + <ref local="cacheManager"/> + </property> + <property name="cacheName"> + <value>ticketCache</value> + </property> +</bean> + +<bean id="statelessTicketCache" class="org.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache"> + <property name="cache"><ref local="ticketCacheBackend"/></property> +</bean> + +<bean id="casAuthoritiesPopulator" class="org.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator"> + <property name="userDetailsService"><ref bean="inMemoryDaoImpl"/></property> +</bean> + +<bean id="casProxyDecider" class="org.acegisecurity.providers.cas.proxy.RejectProxyTickets"/> + + + + The beans are all reasonable self-explanatory if you refer back + to the "How CAS Works" section. Careful readers might notice one + surprise: the statelessTicketCache property of the + CasAuthenticationProvider. This is discussed in + detail in the "Advanced CAS Usage" section. + + Note the CasProxyTicketValidator has a + remarked out trustStore property. This property + might be helpful if you experience HTTPS certificate issues. Also note + the proxyCallbackUrl is set so the service can + receive a proxy-granting ticket. As mentioned above, this is optional + and unnecessary if you do not require proxy-granting tickets. If you + do use this feature, you will need to configure a suitable servlet to + receive the proxy-granting tickets. We suggest you use CAS' + ProxyTicketReceptor by adding the following to your + web application's web.xml: + + +<servlet> + <servlet-name>casproxy</servlet-name> + <servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class> +</servlet> + +<servlet-mapping> + <servlet-name>casproxy</servlet-name> + <url-pattern>/casProxy/*</url-pattern> +</servlet-mapping> + + + + This completes the configuration of CAS. If you haven't made any + mistakes, your web application should happily work within the + framework of CAS single sign on. No other parts of Acegi Security need + to be concerned about the fact CAS handled authentication. + + There is also a contacts-cas.war file in the + sample applications directory. This sample application uses the above + settings and can be deployed to see CAS in operation + + + + Advanced Issues + + The CasAuthenticationProvider distinguishes + between stateful and stateless clients. A stateful client is + considered any that originates via the + CasProcessingFilter. A stateless client is any that + presents an authentication request via the + UsernamePasswordAuthenticationToken with a + principal equal to + CasProcessingFilter.CAS_STATELESS_IDENTIFIER. + + Stateless clients are likely to be via remoting protocols such + as Hessian and Burlap. The BasicProcessingFilter is + still used in this case, but the remoting protocol client is expected + to present a username equal to the static string above, and a password + equal to a CAS service ticket. Clients should acquire a CAS service + ticket directly from the CAS server. + + Because remoting protocols have no way of presenting themselves + within the context of a HttpSession, it isn't + possible to rely on the HttpSession's + HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY + attribute to locate the CasAuthenticationToken. + Furthermore, because the CAS server invalidates a service ticket after + it has been validated by the TicketValidator, + presenting the same service ticket on subsequent requests will not + work. It is similarly very difficult to obtain a proxy-granting ticket + for a remoting protocol client, as they are often deployed on client + machines which rarely have HTTPS URLs that would be accessible to the + CAS server. + + One obvious option is to not use CAS at all for remoting + protocol clients. However, this would eliminate many of the desirable + features of CAS. + + As a middle-ground, the + CasAuthenticationProvider uses a + StatelessTicketCache. This is used solely for + requests with a principal equal to + CasProcessingFilter.CAS_STATELESS_IDENTIFIER. What + happens is the CasAuthenticationProvider will store + the resulting CasAuthenticationToken in the + StatelessTicketCache, keyed on the service ticket. + Accordingly, remoting protocol clients can present the same service + ticket and the CasAuthenticationProvider will not + need to contact the CAS server for validation (aside from the first + request). + + The other aspect of advanced CAS usage involves creating proxy + tickets from the proxy-granting ticket. As indicated above, we + recommend you use CAS' ProxyTicketReceptor to + receive these tickets. The ProxyTicketReceptor + provides a static method that enables you to obtain a proxy ticket by + presenting the proxy-granting IOU ticket. You can obtain the + proxy-granting IOU ticket by calling + CasAuthenticationToken.getProxyGrantingTicketIou(). + + It is hoped you find CAS integration easy and useful with Acegi + Security classes. Welcome to enterprise-wide single sign on! + + + + + Container Adapter Authentication + + + Overview + + Very early versions of Acegi Security exclusively used Container + Adapters for interfacing authentication with end users. Whilst this + worked well, it required considerable time to support multiple + container versions and the configuration itself was relatively + time-consuming for developers. For this reason the HTTP Form + Authentication and HTTP Basic Authentication approaches were + developed, and are today recommended for almost all + applications. + + Container Adapters enable Acegi Security to integrate directly + with the containers used to host end user applications. This + integration means that applications can continue to leverage the + authentication and authorization capabilities built into containers + (such as isUserInRole() and form-based or basic + authentication), whilst benefiting from the enhanced security + interception capabilities provided by Acegi Security (it should be + noted that Acegi Security also offers + ContextHolderAwareRequestWrapper to deliver + isUserInRole() and similar Servlet Specification + compatibility methods). + + The integration between a container and Acegi Security is + achieved through an adapter. The adapter provides a + container-compatible user authentication provider, and needs to return + a container-compatible user object. + + The adapter is instantiated by the container and is defined in a + container-specific configuration file. The adapter then loads a Spring + application context which defines the normal authentication manager + settings, such as the authentication providers that can be used to + authenticate the request. The application context is usually named + acegisecurity.xml and is placed in a + container-specific location. + + Acegi Security currently supports Jetty, Catalina (Tomcat), + JBoss and Resin. Additional container adapters can easily be + written + + + + Adapter Authentication Provider + + As is always the case, the container adapter generated + Authentication object still needs to be + authenticated by an AuthenticationManager when + requested to do so by the + AbstractSecurityInterceptor. The + AuthenticationManager needs to be certain the + adapter-provided Authentication object is valid and + was actually authenticated by a trusted adapter. + + Adapters create Authentication objects which + are immutable and implement the AuthByAdapter + interface. These objects store the hash of a key that is defined by + the adapter. This allows the Authentication object + to be validated by the AuthByAdapterProvider. This + authentication provider is defined as follows: + + <bean id="authByAdapterProvider" class="org.acegisecurity.adapters.AuthByAdapterProvider"> + <property name="key"><value>my_password</value></property> +</bean> + + The key must match the key that is defined in the + container-specific configuration file that starts the adapter. The + AuthByAdapterProvider automatically accepts as + valid any AuthByAdapter implementation that returns + the expected hash of the key. + + To reiterate, this means the adapter will perform the initial + authentication using providers such as + DaoAuthenticationProvider, returning an + AuthByAdapter instance that contains a hash code of + the key. Later, when an application calls a security interceptor + managed resource, the AuthByAdapter instance in the + SecurityContext in the + SecurityContextHolder will be tested by the + application's AuthByAdapterProvider. There is no + requirement for additional authentication providers such as + DaoAuthenticationProvider within the + application-specific application context, as the only type of + Authentication instance that will be presented by + the application is from the container adapter. + + Classloader issues are frequent with containers and the use of + container adapters illustrates this further. Each container requires a + very specific configuration. The installation instructions are + provided below. Once installed, please take the time to try the sample + application to ensure your container adapter is properly + configured. + + When using container adapters with the + DaoAuthenticationProvider, ensure you set its + forcePrincipalAsString property to + true. + + + + Jetty + + The following was tested with Jetty 4.2.18. + + $JETTY_HOME refers to the root of your Jetty + installation. + + Edit your $JETTY_HOME/etc/jetty.xml file so + the <Configure class> section has a new + addRealm call: + + + <Call name="addRealm"> + <Arg> + <New class="org.acegisecurity.adapters.jetty.JettyAcegiUserRealm"> + <Arg>Spring Powered Realm</Arg> + <Arg>my_password</Arg> + <Arg>etc/acegisecurity.xml</Arg> + </New> + </Arg> + </Call> + + + + Copy acegisecurity.xml into + $JETTY_HOME/etc. + + Copy the following files into + $JETTY_HOME/ext: + + aopalliance.jar + + + + commons-logging.jar + + + + spring.jar + + + + acegi-security-jetty-XX.jar + + + + commons-codec.jar + + + + burlap.jar + + + + hessian.jar + + + + None of the above JAR files (or + acegi-security-XX.jar) should be in your + application's WEB-INF/lib. The realm name indicated + in your web.xml does matter with Jetty. The + web.xml must express the same + <realm-name> as your + jetty.xml (in the example above, "Spring Powered + Realm"). + + + + JBoss + + The following was tested with JBoss 3.2.6. + + $JBOSS_HOME refers to the root of your JBoss + installation. + + There are two different ways of making spring context available + to the Jboss integration classes. + + The first approach is by editing your + $JBOSS_HOME/server/your_config/conf/login-config.xml + file so that it contains a new entry under the + <Policy> section: + + +<application-policy name = "SpringPoweredRealm"> + <authentication> + <login-module code = "org.acegisecurity.adapters.jboss.JbossAcegiLoginModule" + flag = "required"> + <module-option name = "appContextLocation">acegisecurity.xml</module-option> + <module-option name = "key">my_password</module-option> + </login-module> + </authentication> +</application-policy> + + + + Copy acegisecurity.xml into + $JBOSS_HOME/server/your_config/conf. + + In this configuration acegisecurity.xml + contains the spring context definition including all the + authentication manager beans. You have to bear in mind though, that + SecurityContext is created and destroyed on each + login request, so the login operation might become costly. + Alternatively, the second approach is to use Spring singleton + capabilities through + org.springframework.beans.factory.access.SingletonBeanFactoryLocator. + The required configuration for this approach is: + + +<application-policy name = "SpringPoweredRealm"> + <authentication> + <login-module code = "org.acegisecurity.adapters.jboss.JbossAcegiLoginModule" + flag = "required"> + <module-option name = "singletonId">springRealm</module-option> + <module-option name = "key">my_password</module-option> + <module-option name = "authenticationManager">authenticationManager</module-option> + </login-module> + </authentication> +</application-policy> + + + + In the above code fragment, + authenticationManager is a helper property that + defines the expected name of the + AuthenticationManager in case you have several + defined in the IoC container. The singletonId + property references a bean defined in a + beanRefFactory.xml file. This file needs to be + available from anywhere on the JBoss classpath, including + $JBOSS_HOME/server/your_config/conf. The + beanRefFactory.xml contains the following + declaration: + + +<beans> + <bean id="springRealm" singleton="true" lazy-init="true" class="org.springframework.context.support.ClassPathXmlApplicationContext"> + <constructor-arg> + <list> + <value>acegisecurity.xml</value> + </list> + </constructor-arg> + </bean> +</beans> + + + + Finally, irrespective of the configuration approach you need to + copy the following files into + $JBOSS_HOME/server/your_config/lib: + + aopalliance.jar + + + + spring.jar + + + + acegi-security-jboss-XX.jar + + + + commons-codec.jar + + + + burlap.jar + + + + hessian.jar + + + + None of the above JAR files (or + acegi-security-XX.jar) should be in your + application's WEB-INF/lib. The realm name indicated + in your web.xml does not matter with JBoss. + However, your web application's + WEB-INF/jboss-web.xml must express the same + <security-domain> as your + login-config.xml. For example, to match the above + example, your jboss-web.xml would look like + this: + + +<jboss-web> + <security-domain>java:/jaas/SpringPoweredRealm</security-domain> +</jboss-web> + + JBoss is a widely-used container adapter (mostly due to the need + to support legacy EJBs), so please let us know if you have any + difficulties. + + + + Resin + + The following was tested with Resin 3.0.6. + + $RESIN_HOME refers to the root of your Resin + installation. + + Resin provides several ways to support the container adapter. In + the instructions below we have elected to maximise consistency with + other container adapter configurations. This will allow Resin users to + simply deploy the sample application and confirm correct + configuration. Developers comfortable with Resin are naturally able to + use its capabilities to package the JARs with the web application + itself, and/or support single sign-on. + + Copy the following files into + $RESIN_HOME/lib: + + aopalliance.jar + + + + commons-logging.jar + + + + spring.jar + + + + acegi-security-resin-XX.jar + + + + commons-codec.jar + + + + burlap.jar + + + + hessian.jar + + + + Unlike the container-wide acegisecurity.xml + files used by other container adapters, each Resin web application + will contain its own + WEB-INF/resin-acegisecurity.xml file. Each web + application will also contain a resin-web.xml file + which Resin uses to start the container adapter: + + +<web-app> + <authenticator> + <type>org.acegisecurity.adapters.resin.ResinAcegiAuthenticator</type> + <init> + <app-context-location>WEB-INF/resin-acegisecurity.xml</app-context-location> + <key>my_password</key> + </init> + </authenticator> +</web-app> + + + + With the basic configuration provided above, none of the JAR + files listed (or acegi-security-XX.jar) should be + in your application's WEB-INF/lib. The realm name + indicated in your web.xml does not matter with + Resin, as the relevant authentication class is indicated by the + <authenticator> setting + + + + Tomcat + + The following was tested with Jakarta Tomcat 4.1.30 and + 5.0.19. + + $CATALINA_HOME refers to the root of your + Catalina (Tomcat) installation. + + Edit your $CATALINA_HOME/conf/server.xml file + so the <Engine> section contains only one + active <Realm> entry. An example realm + entry: + + <Realm className="org.acegisecurity.adapters.catalina.CatalinaAcegiUserRealm" + appContextLocation="conf/acegisecurity.xml" + key="my_password" /> + + Be sure to remove any other <Realm> + entry from your <Engine> section. + + Copy acegisecurity.xml into + $CATALINA_HOME/conf. + + Copy acegi-security-catalina-XX.jar into + $CATALINA_HOME/server/lib. + + Copy the following files into + $CATALINA_HOME/common/lib: + + + + aopalliance.jar + + + + spring.jar + + + + commons-codec.jar + + + + burlap.jar + + + + hessian.jar + + + + None of the above JAR files (or + acegi-security-XX.jar) should be in your + application's WEB-INF/lib. The realm name indicated + in your web.xml does not matter with + Catalina. + + We have received reports of problems using this Container + Adapter with Mac OS X. A work-around is to use a script such as + follows: + + #!/bin/sh +export CATALINA_HOME="/Library/Tomcat" +export JAVA_HOME="/Library/Java/Home" +cd / +$CATALINA_HOME/bin/startup.sh + + Finally, restart Tomcat. + + + + + + Authorization + + + The advanced authorization capabilities within Acegi Security + represent one of the most compelling reasons for its popularity. + Irrespective of how you choose to authenticate - whether using an Acegi + Security-provided mechanism and provider, or integrating with a + container or other non-Acegi Security authentication authority - you + will find the authorization services can be used within your application + in a consistent and simple way. + + In this part we'll explore the different + AbstractSecurityInterceptor implementations, which + were introduced in Part I. We then move on to explore how to fine-tune + authorization through use of domain access control lists. + + + + Common Authorization Concepts + + + Authorities + + 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 + AccessDecisionManagers when making authorization + decisions. + + GrantedAuthority is an interface with only + one method: + + public 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 "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. + + Acegi 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 + + The AccessDecisionManager is called by the + AbstractSecurityInterceptor and is responsible for + making final access control decisions. The + AccessDecisionManager interface contains three + methods: + + public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) throws AccessDeniedException; +public boolean supports(ConfigAttribute attribute); +public 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, Acegi Security includes several + AccessDecisionManager implementations that are + based on voting. Figure 4 illustrates the relevant classes. + + + + + + + + Figure 4: Voting Decision Manager + + + + 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: + + public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config); +public boolean supports(ConfigAttribute attribute); +public 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 Acegi 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 + (ie 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. + + There are two concrete AccessDecisionVoter + implementations provided with Acegi Security. The + RoleVoter class will vote if any ConfigAttribute + begins with 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. + + BasicAclEntryVoter is the other concrete + voter included with Acegi Security. It integrates with Acegi + Security's AclManager (discussed later). This voter + is designed to have multiple instances in the same application + context, such as: + + <bean id="aclContactReadVoter" class="org.acegisecurity.vote.BasicAclEntryVoter"> + <property name="processConfigAttribute"><value>ACL_CONTACT_READ</value></property> + <property name="processDomainObjectClass"><value>sample.contact.Contact</value></property> + <property name="aclManager"><ref local="aclManager"/></property> + <property name="requirePermission"> + <list> + <ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/> + <ref local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/> + </list> + </property> +</bean> + +<bean id="aclContactDeleteVoter" class="org.acegisecurity.vote.BasicAclEntryVoter"> + <property name="processConfigAttribute"><value>ACL_CONTACT_DELETE</value></property> + <property name="processDomainObjectClass"><value>sample.contact.Contact</value></property> + <property name="aclManager"><ref local="aclManager"/></property> + <property name="requirePermission"> + <list> + <ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/> + <ref local="org.acegisecurity.acl.basic.SimpleAclEntry.DELETE"/> + </list> + </property> +</bean> + + In the above example, you'd define + ACL_CONTACT_READ or + ACL_CONTACT_DELETE against some methods on a + MethodSecurityInterceptor or + AspectJSecurityInterceptor. When those methods are + invoked, the above applicable voter defined above would vote to grant + or deny access. The voter would look at the method invocation to + locate the first argument of type + sample.contact.Contact, and then pass that + Contact to the AclManager. The + AclManager will then return an access control list + (ACL) that applies to the current Authentication. + Assuming that ACL contains one of the listed + requirePermissions, the voter will vote to grant + access. If the ACL does not contain one of the permissions defined + against the voter, the voter will vote to deny access. + BasicAclEntryVoter is an important class as it + allows you to build truly complex applications with domain object + security entirely defined in the application context. If you're + interested in learning more about Acegi Security's ACL capabilities + and how best to apply them, please see the ACL and "After Invocation" + sections of this reference guide, and the Contacts sample + application. + + It is also possible to implement a custom + AccessDecisionVoter. Several examples are provided + in Acegi 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. + + TODO: Remove references to the old ACL package when it's + deprecated, and have all references to the replacement package limited + to the chapter describing the new ACL implementation. + + + + After Invocation Handling + + 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, Acegi Security provides a convenient hook that has + several concrete implementations that integrate with its ACL + capabilities. + + Figure 5 illustrates Acegi Security's + AfterInvocationManager and its concrete + implementations. + + + + + + + + Figure 5: After Invocation Implementation + + + + Like many other parts of Acegi Security, + AfterInvocationManager has a single concrete + implementation, AfterInvocationProvider, 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. 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 Acegi 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 + + + ACL-Aware AfterInvocationProviders + + 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.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.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.acegisecurity.afterinvocation.AclEntryAfterInvocationProvider"> + <constructor-arg> + <ref bean="aclService"/> + </constructor-arg> + <constructor-arg> + <list> + <ref local="org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION"/> + <ref local="org.acegisecurity.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 requirePermissions is not held by the + Authentication. The + AclEntryAfterInvocationProvider queries the + AclService 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.acegisecurity.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider"> + <constructor-arg> + <ref bean="aclService"/> + </constructor-arg> + <constructor-arg> + <list> + <ref local="org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION"/> + <ref local="org.acegisecurity.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 + requirePermissions. + + The Contacts sample application demonstrates these two + AfterInvocationProviders. + + + + ACL-Aware AfterInvocationProviders (old ACL module) + + 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.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.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.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationProvider"> + <property name="aclManager"><ref local="aclManager"/></property> + <property name="requirePermission"> + <list> + <ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/> + <ref local="org.acegisecurity.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 requirePermissions 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.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider"> + <property name="aclManager"><ref local="aclManager"/></property> + <property name="requirePermission"> + <list> + <ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/> + <ref local="org.acegisecurity.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 + requirePermissions. + + The Contacts sample application demonstrates these two + AfterInvocationProviders. + + + + + Authorization Tag Libraries + + AuthorizeTag is used to include content if + the current principal holds certain + GrantedAuthoritys. + + The following JSP fragment illustrates how to use the + AuthorizeTag: + + <authz:authorize ifAllGranted="ROLE_SUPERVISOR"> + <td> + <A HREF="del.htm?id=<c:out value="${contact.id}"/>">Del</A> + </td> +</authz:authorize> + + This tag would cause the tag's body to be output if the + principal has been granted ROLE_SUPERVISOR. + + The authz: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: + + <authz: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> +</authz: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 Acegi 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. + + + + + Secure Object Implementations + + + AOP Alliance (MethodInvocation) Security Interceptor + + To secure MethodInvocations, developers + simply add a properly configured + MethodSecurityInterceptor into the application + context. Next the beans requiring security are chained into the + interceptor. This chaining is accomplished using Spring’s + ProxyFactoryBean or + BeanNameAutoProxyCreator, as commonly used by many + other parts of Spring (refer to the sample application for examples). + Alternatively, Acegi Security provides a + MethodDefinitionSourceAdvisor which may be used + with Spring's DefaultAdvisorAutoProxyCreator to + automatically chain the security interceptor in front of any beans + defined against the MethodSecurityInterceptor. The + MethodSecurityInterceptor itself is configured as + follows: + + <bean id="bankManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> + <property name="validateConfigAttributes"><value>true</value></property> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> + <property name="runAsManager"><ref bean="runAsManager"/></property> + <property name="afterInvocationManager"><ref bean="afterInvocationManager"/></property> + <property name="objectDefinitionSource"> + <value> + org.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR,RUN_AS_SERVER + org.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_AS_SERVER + </value> + </property> +</bean> + + As shown above, the MethodSecurityInterceptor + is configured with a reference to an + AuthenticationManager, + AccessDecisionManager and + RunAsManager, which are each discussed in separate + sections below. In this case we've also defined an + AfterInvocationManager, although this is entirely + optional. The MethodSecurityInterceptor is also + configured with configuration attributes that apply to different + method signatures. A full discussion of configuration attributes is + provided in the High Level Design section of this document. + + The MethodSecurityInterceptor can be + configured with configuration attributes in three ways. The first is + via a property editor and the application context, which is shown + above. The second is via defining the configuration attributes in your + source code using Jakarta Commons Attributes or Java 5 Annotations. + The third is via writing your own + ObjectDefinitionSource, although this is beyond the + scope of this document. Irrespective of the approach used, the + ObjectDefinitionSource is responsible for returning + a ConfigAttributeDefinition object that contains + all of the configuration attributes associated with a single secure + method. + + It should be noted that the + MethodSecurityInterceptor.setObjectDefinitionSource() + method actually expects an instance of + MethodDefinitionSource. This is a marker interface + which subclasses ObjectDefinitionSource. It simply + denotes the ObjectDefinitionSource understands + MethodInvocations. In the interests of simplicity + we'll continue to refer to the + MethodDefinitionSource as an + ObjectDefinitionSource, as the distinction is of + little relevance to most users of the + MethodSecurityInterceptor. + + If using the application context property editor approach (as + shown above), commas are used to delimit the different configuration + attributes that apply to a given method pattern. Each configuration + attribute is assigned into its own SecurityConfig + object. The SecurityConfig object is discussed in + the High Level Design section. + + If you are using the Jakarta Commons Attributes approach, your + bean context will be configured differently: + + <bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/> +<bean id="objectDefinitionSource" class="org.acegisecurity.intercept.method.MethodDefinitionAttributes"> + <property name="attributes"><ref local="attributes"/></property> +</bean> + +<bean id="bankManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> + <property name="validateConfigAttributes"><value>false</value></property> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> + <property name="runAsManager"><ref bean="runAsManager"/></property> + <property name="objectDefinitionSource"><ref bean="objectDefinitionSource"/></property> +</bean> + + In addition, your source code will contain Jakarta Commons + Attributes tags that refer to a concrete implementation of + ConfigAttribute. The following example uses the + SecurityConfig implementation to represent the + configuration attributes, and results in the same security + configuration as provided by the property editor approach + above: + + public interface BankManager { + + /** + * @@SecurityConfig("ROLE_SUPERVISOR") + * @@SecurityConfig("RUN_AS_SERVER") + */ + public void deleteSomething(int id); + + /** + * @@SecurityConfig("ROLE_SUPERVISOR") + * @@SecurityConfig("RUN_AS_SERVER") + */ + public void deleteAnother(int id); + + /** + * @@SecurityConfig("ROLE_TELLER") + * @@SecurityConfig("ROLE_SUPERVISOR") + * @@SecurityConfig("BANKSECURITY_CUSTOMER") + * @@SecurityConfig("RUN_AS_SERVER") + */ + public float getBalance(int id); +} + + If you are using the Acegi Security Java 5 Annotations approach, + your bean context will be configured as follows: + + <bean id="attributes" class="org.acegisecurity.annotation.SecurityAnnotationAttributes"/> +<bean id="objectDefinitionSource" class="org.acegisecurity.intercept.method.MethodDefinitionAttributes"> + <property name="attributes"><ref local="attributes"/></property> +</bean> + +<bean id="bankManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> + <property name="validateConfigAttributes"><value>false</value></property> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> + <property name="runAsManager"><ref bean="runAsManager"/></property> + <property name="objectDefinitionSource"><ref bean="objectDefinitionSource"/></property> +</bean> + + In addition, your source code will contain Acegi Java 5 Security + Annotations that represent the ConfigAttribute. The + following example uses the @Secured annotations to + represent the configuration attributes, and results in the same + security configuration as provided by the property editor + approach: + + import org.acegisecurity.annotation.Secured; + +public interface BankManager { + + /** + * Delete something + */ + @Secured({"ROLE_SUPERVISOR","RUN_AS_SERVER" }) + public void deleteSomething(int id); + + /** + * Delete another + */ + @Secured({"ROLE_SUPERVISOR","RUN_AS_SERVER" }) + public void deleteAnother(int id); + + /** + * Get balance + */ + @Secured({"ROLE_TELLER","ROLE_SUPERVISOR","BANKSECURITY_CUSTOMER","RUN_AS_SERVER" }) + public float getBalance(int id); +} + + You might have noticed the + validateConfigAttributes property in the above + MethodSecurityInterceptor examples. When set to + true (the default), at startup time the + MethodSecurityInterceptor will evaluate if the + provided configuration attributes are valid. It does this by checking + each configuration attribute can be processed by either the + AccessDecisionManager or the + RunAsManager. If neither of these can process a + given configuration attribute, an exception is thrown. If using the + Jakarta Commons Attributes method of configuration, you should set + validateConfigAttributes to + false. + + Please note that when using + BeanNameAutoProxyCreator to create the required + proxy for security, the configuration must contain the property + proxyTargetClass set to true. + Otherwise, the method passed to + MethodSecurityInterceptor.invoke is the proxy's + caller, not the proxy's target. Note that this introduces a + requirement on CGLIB. See an example of using + BeanNameAutoProxyCreator below: + + <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> + <property name="interceptorNames"> + <list><value>methodSecurityInterceptor</value></list> + </property> + <property name="beanNames"> + <list><value>targetObjectName</value></list> + </property> + <property name="proxyTargetClass" value="true"/> +</bean> + + + + AspectJ (JoinPoint) Security Interceptor + + The AspectJ security interceptor is very similar to the AOP + Alliance security interceptor discussed in the previous section. + Indeed we will only discuss the differences in this section. + + The AspectJ interceptor is named + AspectJSecurityInterceptor. Unlike the AOP Alliance + security interceptor, which relies on the Spring application context + to weave in the security interceptor via proxying, the + AspectJSecurityInterceptor is weaved in via the + AspectJ compiler. It would not be uncommon to use both types of + security interceptors in the same application, with + AspectJSecurityInterceptor being used for domain + object instance security and the AOP Alliance + MethodSecurityInterceptor being used for services + layer security. + + Let's first consider how the + AspectJSecurityInterceptor is configured in the + Spring application context: + + <bean id="bankManagerSecurity" class="org.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor"> + <property name="validateConfigAttributes"><value>true</value></property> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> + <property name="runAsManager"><ref bean="runAsManager"/></property> + <property name="afterInvocationManager"><ref bean="afterInvocationManager"/></property> + <property name="objectDefinitionSource"> + <value> + org.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR,RUN_AS_SERVER + org.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_AS_SERVER + </value> + </property> +</bean> + + As you can see, aside from the class name, the + AspectJSecurityInterceptor is exactly the same as + the AOP Alliance security interceptor. Indeed the two interceptors can + share the same objectDefinitionSource, as the + ObjectDefinitionSource works with + java.lang.reflect.Methods rather than an AOP + library-specific class. Of course, your access decisions have access + to the relevant AOP library-specific invocation (ie + MethodInvocation or JoinPoint) + and as such can consider a range of addition criteria when making + access decisions (such as method arguments). + + Next you'll need to define an AspectJ aspect. + For example: + + package org.acegisecurity.samples.aspectj; + +import org.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor; +import org.acegisecurity.intercept.method.aspectj.AspectJCallback; +import org.springframework.beans.factory.InitializingBean; + +public aspect DomainObjectInstanceSecurityAspect implements InitializingBean { + + private AspectJSecurityInterceptor securityInterceptor; + + pointcut domainObjectInstanceExecution(): target(PersistableEntity) + && execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect); + + Object around(): domainObjectInstanceExecution() { + if (this.securityInterceptor != null) { + AspectJCallback callback = new AspectJCallback() { + public Object proceedWithObject() { + return proceed(); + } + }; + return this.securityInterceptor.invoke(thisJoinPoint, callback); + } else { + return proceed(); + } + } + + public AspectJSecurityInterceptor getSecurityInterceptor() { + return securityInterceptor; + } + + public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) { + this.securityInterceptor = securityInterceptor; + } + + public void afterPropertiesSet() throws Exception { + if (this.securityInterceptor == null) + throw new IllegalArgumentException("securityInterceptor required"); + } +} + + In the above example, the security interceptor will be applied + to every instance of PersistableEntity, which is an + abstract class not shown (you can use any other class or + pointcut expression you like). For those curious, + AspectJCallback is needed because the + proceed(); statement has special meaning only + within an around() body. The + AspectJSecurityInterceptor calls this anonymous + AspectJCallback class when it wants the target + object to continue. + + You will need to configure Spring to load the aspect and wire it + with the AspectJSecurityInterceptor. A bean + declaration which achieves this is shown below: + + +<bean id="domainObjectInstanceSecurityAspect" + class="org.acegisecurity.samples.aspectj.DomainObjectInstanceSecurityAspect" + factory-method="aspectOf"> + <property name="securityInterceptor"><ref bean="aspectJSecurityInterceptor"/></property> +</bean> + + + + That's it! Now you can create your beans from anywhere within + your application, using whatever means you think fit (eg new + Person();) and they will have the security interceptor + applied. + + + + FilterInvocation Security Interceptor + + To secure FilterInvocations, developers need + to add a filter to their web.xml that delegates to + the FilterSecurityInterceptor. A typical + configuration example is provided below: + + <filter> + <filter-name>Acegi HTTP Request Security Filter</filter-name> + <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class> + <init-param> + <param-name>targetClass</param-name> + <param-value>org.acegisecurity.intercept.web.FilterSecurityInterceptor</param-value> + </init-param> +</filter> + +<filter-mapping> + <filter-name>Acegi HTTP Request Security Filter</filter-name> + <url-pattern>/*</url-pattern> +</filter-mapping> + + Notice that the filter is actually a + FilterToBeanProxy. Most of the filters used by + Acegi Security use this class. Refer to the Filters section to learn + more about this bean. + + In the application context you will need to configure three + beans: + + <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> + <property name="authenticationEntryPoint"><ref local="authenticationEntryPoint"/></property> +</bean> + +<bean id="authenticationEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> + <property name="loginFormUrl"><value>/acegilogin.jsp</value></property> + <property name="forceHttps"><value>false</value></property> +</bean> + +<bean id="filterSecurityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> + <property name="objectDefinitionSource"> + <value> + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + \A/secure/super/.*\Z=ROLE_WE_DONT_HAVE + \A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER + </value> + </property> +</bean> + + The ExceptionTranslationFilter provides + the bridge between Java exceptions and HTTP responses. It is solely + concerned with maintaining the user interface. This filter does not do + any actual security enforcement. If an + AuthenticationException is detected, + the filter will call the AuthenticationEntryPoint to commence the + authentication process (e.g. a user login). + + The AuthenticationEntryPoint will be called + if the user requests a secure HTTP resource but they are not + authenticated. The class handles presenting the appropriate response + to the user so that authentication can begin. Three concrete + implementations are provided with Acegi Security: + AuthenticationProcessingFilterEntryPoint for + commencing a form-based authentication, + BasicProcessingFilterEntryPoint for commencing a + HTTP Basic authentication process, and + CasProcessingFilterEntryPoint for commencing a + JA-SIG Central Authentication Service (CAS) login. The + AuthenticationProcessingFilterEntryPoint and + CasProcessingFilterEntryPoint have optional + properties related to forcing the use of HTTPS, so please refer to the + JavaDocs if you require this. + + FilterSecurityInterceptor is responsible for + handling the security of HTTP resources. Like any other security + interceptor, it requires a reference to an + AuthenticationManager and an + AccessDecisionManager, which are both discussed in + separate sections below. The + FilterSecurityInterceptor is also configured with + configuration attributes that apply to different HTTP URL requests. A + full discussion of configuration attributes is provided in the High + Level Design section of this document. + + The FilterSecurityInterceptor can be + configured with configuration attributes in two ways. The first is via + a property editor and the application context, which is shown above. + The second is via writing your own + ObjectDefinitionSource, although this is beyond the + scope of this document. Irrespective of the approach used, the + ObjectDefinitionSource is responsible for returning + a ConfigAttributeDefinition object that contains + all of the configuration attributes associated with a single secure + HTTP URL. + + It should be noted that the + FilterSecurityInterceptor.setObjectDefinitionSource() + method actually expects an instance of + FilterInvocationDefinitionSource. This is a marker + interface which subclasses ObjectDefinitionSource. + It simply denotes the ObjectDefinitionSource + understands FilterInvocations. In the interests of + simplicity we'll continue to refer to the + FilterInvocationDefinitionSource as an + ObjectDefinitionSource, as the distinction is of + little relevance to most users of the + FilterSecurityInterceptor. + + If using the application context property editor approach (as + shown above), commas are used to delimit the different configuration + attributes that apply to each HTTP URL. Each configuration attribute + is assigned into its own SecurityConfig object. The + SecurityConfig object is discussed in the High + Level Design section. The ObjectDefinitionSource + created by the property editor, + FilterInvocationDefinitionSource, matches + configuration attributes against FilterInvocations + based on expression evaluation of the request URL. Two standard + expression syntaxes are supported. The default is to treat all + expressions as regular expressions. Alternatively, the presence of a + PATTERN_TYPE_APACHE_ANT directive will cause all + expressions to be treated as Apache Ant paths. It is not possible to + mix expression syntaxes within the same definition. For example, the + earlier configuration could be generated using Apache Ant paths as + follows: + + <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> + <property name="runAsManager"><ref bean="runAsManager"/></property> + <property name="objectDefinitionSource"> + <value> + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT + /secure/super/**=ROLE_WE_DONT_HAVE + /secure/**=ROLE_SUPERVISOR,ROLE_TELLER + </value> + </property> +</bean> + + Irrespective of the type of expression syntax used, expressions + are always evaluated in the order they are defined. Thus it is + important that more specific expressions are defined higher in the + list than less specific expressions. This is reflected in our example + above, where the more specific /secure/super/ + pattern appears higher than the less specific + /secure/ pattern. If they were reversed, the + /secure/ pattern would always match and the + /secure/super/ pattern would never be + evaluated. + + The special keyword + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON causes + the FilterInvocationDefinitionSource to + automatically convert a request URL to lowercase before comparison + against the expressions. Whilst by default the case of the request URL + is not converted, it is generally recommended to use + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON and + write each expression assuming lowercase. + + As with other security interceptors, the + validateConfigAttributes property is observed. When + set to true (the default), at startup time the + FilterSecurityInterceptor will evaluate if the + provided configuration attributes are valid. It does this by checking + each configuration attribute can be processed by either the + AccessDecisionManager or the + RunAsManager. If neither of these can process a + given configuration attribute, an exception is thrown. + + + + + Domain Object Security + +
+ Overview + + 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.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.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. + + 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 Acegi 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 Customers. If a user + might be able to access 5,000 Customers (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. +
+ +
+ Key Concepts + + The org.acegisecurity.acls package should be consulted for its + major interfaces. The key interfaces are: + + + + Acl: Every domain object has one and only + one Acl object, which internally holds the + AccessControlEntrys as well as knows the owner + of the Acl. An Acl does not refer directly to + the domain object, but instead to an + ObjectIdentity. + + + + AccessControlEntry: An + Acl holds multiple AccessControlEntrys, which + are often abbreviated as ACEs in the framework. Each ACE refers to + a specific tuple of Permission, + Sid and Acl. An ACE can also + be granting or non-granting and contain audit settings. + + + + Permission: A permission represents an + immutable particular bit mask, and offers convenience functions + for bit masking and outputting information. + + + + Sid: The ACL module needs to refer to + principals and GrantedAuthority[]s. A level of + indirection is provided by the Sid interface. + Common classes include PrincipalSid (to + represent the principal inside an + Authentication object) and + GrantedAuthoritySid. + + + + ObjectIdentity: Each domain object is + represented internally within the ACL module by an + ObjectIdentity. + + + + AclService: Retrieves the + Acl applicable for a given + ObjectIdentity. + + + + MutableAclService: Allows a modified + Acl to be presented for persistence. It is not + essential to use this interface if you do not wish. + + + + The ACL module was based on extensive feedback from the user + community following real-world use of the original ACL module. This + feedback resulted in a rearchitecture of the ACL module to offer + significantly enhanced performance (particularly in the area of + database retrieval), significantly better encapsulation, higher + cohesion, and enhanced customisation points. + + The Contacts Sample that ships with Acegi Security 1.0.3 offers + a demonstration of the new ACL module. Converting Contacts from using + the old module to the new module was relatively simple, and users of + the old ACL module will likely find their applications can be modified + with relatively little work. + + We will document the new ACL module more fully with a subsequent + release. Please note that the new ACL module should be considered a + preview only (ie do not use in production without proper prior + testing), and there is a small chance there may be changes between + 1.0.3 and 1.1.0 when it will become final. Nevertheless, + compatibility-affecting changes are considered quite unlikely, + especially given the module is already based on several years of + feedback from users of the original ACL module. +
+
+ + + Domain Object Security (old ACL module) + +
+ Overview + + 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.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.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. + + 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 Acegi 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 Customers. If a user + might be able to access 5,000 Customers (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. +
+ +
+ Basic ACL Package + + Please note that our Basic ACL services are currently being + refactored. We expect release 1.1.0 will contain this new code. + Planned code is already in the Acegi Security Subversion sandbox, so + please check there if you have a new application requiring ACLs or are + in the planning stages. The Basic ACL services will be deprecated from + release 1.1.0. + + The org.acegisecurity.acl package is very + simple, comprising only a handful of interfaces and a single class, as + shown in Figure 6. It provides the basic foundation for access control + list (ACL) lookups. + + + + + + + + Figure 6: Access Control List Manager + + + + The central interface is AclManager, which is + defined by two methods: + + public AclEntry[] getAcls(java.lang.Object domainInstance); +public AclEntry[] getAcls(java.lang.Object domainInstance, Authentication authentication); + + AclManager is intended to be used as a + collaborator against your business objects, or, more desirably, + AccessDecisionVoters. This means you use Spring's + normal ApplicationContext features to wire up your + AccessDecisionVoter (or business method) with an + AclManager. Consideration was given to placing the + ACL information in the ContextHolder, but it was + felt this would be inefficient both in terms of memory usage as well + as the time spent loading potentially unused ACL information. The + trade-off of needing to wire up a collaborator for those objects + requiring ACL information is rather minor, particularly in a + Spring-managed application. + + The first method of the AclManager will + return all ACLs applying to the domain object instance passed to it. + The second method does the same, but only returns those ACLs which + apply to the passed Authentication object. + + The AclEntry interface returned by + AclManager is merely a marker interface. You will + need to provide an implementation that reflects that ACL permissions + for your application. + + Rounding out the org.acegisecurity.acl + package is an AclProviderManager class, with a + corresponding AclProvider interface. + AclProviderManager is a concrete implementation of + AclManager, which iterates through registered + AclProviders. The first + AclProvider that indicates it can authoritatively + provide ACL information for the presented domain object instance will + be used. This is very similar to the + AuthenticationProvider interface used for + authentication. + + With this background, let's now look at a usable ACL + implementation. + + Acegi Security includes a production-quality ACL provider + implementation, which is shown in Figure 7. + + + + + + + + Figure 7: Basic ACL Manager + + + + The implementation is based on integer masking, which is + commonly used for ACL permissions given its flexibility and speed. + Anyone who has used Unix's chmod command will know + all about this type of permission masking (eg chmod + 777). You'll find the classes and interfaces for the integer + masking ACL package under + org.acegisecurity.acl.basic. + + Extending the AclEntry interface is a + BasicAclEntry interface, with the main methods + shown below: + + public AclObjectIdentity getAclObjectIdentity(); +public AclObjectIdentity getAclObjectParentIdentity(); +public int getMask(); +public java.lang.Object getRecipient(); + + As shown, each BasicAclEntry has four main + properties. The mask is the integer that represents + the permissions granted to the recipient. The + aclObjectIdentity is able to identify the domain + object instance for which the ACL applies, and the + aclObjectParentIdentity optionally specifies the + parent of the domain object instance. Multiple + BasicAclEntrys usually exist against a single + domain object instance, and as suggested by the parent identity + property, permissions granted higher in the object hierarchy will + trickle down and be inherited (unless blocked by integer zero). + + BasicAclEntry implementations typically + provide convenience methods, such as + isReadAllowed(), to avoid application classes + needing to perform bit masking themselves. The + SimpleAclEntry and + AbstractBasicAclEntry demonstrate and provide much + of this bit masking logic. + + The AclObjectIdentity itself is merely a + marker interface, so you need to provide implementations for your + domain objects. However, the package does include a + NamedEntityObjectIdentity implementation which will + suit many needs. The NamedEntityObjectIdentity + identifies a given domain object instance by the classname of the + instance and the identity of the instance. A + NamedEntityObjectIdentity can be constructed + manually (by calling the constructor and providing the classname and + identity Strings), or by passing in any domain + object that contains a getId() method. + + The actual AclProvider implementation is + named BasicAclProvider. It has adopted a similar + design to that used by the authentication-related + DaoAuthenticationProvder. Specifically, you define + a BasicAclDao against the provider, so different + ACL repository types can be accessed in a pluggable manner. The + BasicAclProvider also supports pluggable cache + providers (with Acegi Security including an implementation that fronts + EH-CACHE). + + The BasicAclDao interface is very simple to + implement: + + public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity); + + A BasicAclDao implementation needs to + understand the presented AclObjectIdentity and how + it maps to a storage repository, find the relevant records, and create + appropriate BasicAclEntry objects and return + them. + + Acegi Security includes a single BasicAclDao + implementation called JdbcDaoImpl. As implied by + the name, JdbcDaoImpl accesses ACL information from + a JDBC database. There is also an extended version of this DAO, + JdbcExtendedDaoImpl, which provides CRUD operations + on the JDBC database, although we won't discuss these features here. + The default database schema and some sample data will aid in + understanding its function: + + CREATE TABLE acl_object_identity ( + id IDENTITY NOT NULL, + object_identity VARCHAR_IGNORECASE(250) NOT NULL, + parent_object INTEGER, + acl_class VARCHAR_IGNORECASE(250) NOT NULL, + CONSTRAINT unique_object_identity UNIQUE(object_identity), + FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id) +); + +CREATE TABLE acl_permission ( + id IDENTITY NOT NULL, + acl_object_identity INTEGER NOT NULL, + recipient VARCHAR_IGNORECASE(100) NOT NULL, + mask INTEGER NOT NULL, + CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient), + FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id) +); + +INSERT INTO acl_object_identity VALUES (1, 'corp.DomainObject:1', null, 'org.acegisecurity.acl.basic.SimpleAclEntry'); +INSERT INTO acl_object_identity VALUES (2, 'corp.DomainObject:2', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry'); +INSERT INTO acl_object_identity VALUES (3, 'corp.DomainObject:3', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry'); +INSERT INTO acl_object_identity VALUES (4, 'corp.DomainObject:4', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry'); +INSERT INTO acl_object_identity VALUES (5, 'corp.DomainObject:5', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry'); +INSERT INTO acl_object_identity VALUES (6, 'corp.DomainObject:6', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry'); + +INSERT INTO acl_permission VALUES (null, 1, 'ROLE_SUPERVISOR', 1); +INSERT INTO acl_permission VALUES (null, 2, 'ROLE_SUPERVISOR', 0); +INSERT INTO acl_permission VALUES (null, 2, 'marissa', 2); +INSERT INTO acl_permission VALUES (null, 3, 'scott', 14); +INSERT INTO acl_permission VALUES (null, 6, 'scott', 1); + + As can be seen, database-specific constraints are used + extensively to ensure the integrity of the ACL information. If you + need to use a different database (Hypersonic SQL statements are shown + above), you should try to implement equivalent constraints. The + equivalent Oracle configuration is: + + CREATE TABLE ACL_OBJECT_IDENTITY ( + ID number(19,0) not null, + OBJECT_IDENTITY varchar2(255) NOT NULL, + PARENT_OBJECT number(19,0), + ACL_CLASS varchar2(255) NOT NULL, + primary key (ID) +); +ALTER TABLE ACL_OBJECT_IDENTITY ADD CONTRAINT FK_PARENT_OBJECT foreign key (ID) references ACL_OBJECT_IDENTITY + +CREATE SEQUENCE ACL_OBJECT_IDENTITY_SEQ; + +CREATE OR REPLACE TRIGGER ACL_OBJECT_IDENTITY_ID +BEFORE INSERT ON ACL_OBJECT_IDENTITY +FOR EACH ROW +BEGIN + SELECT ACL_OBJECT_IDENTITY_SEQ.NEXTVAL INTO :new.id FROM dual; +END; + +CREATE TABLE ACL_PERMISSION ( + ID number(19,0) not null, + ACL_OBJECT_IDENTITY number(19,0) NOT NULL, + RECIPIENT varchar2(255) NOT NULL, + MASK number(19,0) NOT NULL, + primary key (ID) +); + +ALTER TABLE ACL_PERMISSION ADD CONTRAINT UNIQUE_ID_RECIPIENT unique (acl_object_identity, recipient); + +CREATE SEQUENCE ACL_PERMISSION_SEQ; + +CREATE OR REPLACE TRIGGER ACL_PERMISSION_ID +BEFORE INSERT ON ACL_PERMISSION +FOR EACH ROW +BEGIN + SELECT ACL_PERMISSION_SEQ.NEXTVAL INTO :new.id FROM dual; +END; + +<bean id="basicAclExtendedDao" class="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl"> + <property name="dataSource"> + <ref bean="dataSource"/> + </property> + <property name="objectPropertiesQuery" value="${acegi.objectPropertiesQuery}"/> +</bean> + +<prop key="acegi.objectPropertiesQuery">SELECT CHILD.ID, CHILD.OBJECT_IDENTITY, CHILD.ACL_CLASS, PARENT.OBJECT_IDENTITY as PARENT_OBJECT_IDENTITY FROM acl_object_identity as CHILD LEFT OUTER JOIN acl_object_identity as PARENT ON CHILD.parent_object=PARENT.id WHERE CHILD.object_identity = ?</prop> + + The JdbcDaoImpl will only respond to requests + for NamedEntityObjectIdentitys. It converts such + identities into a single String, comprising + the NamedEntityObjectIdentity.getClassname() + + ":" + + NamedEntityObjectIdentity.getId(). This yields the + type of object_identity values shown above. As + indicated by the sample data, each database row corresponds to a + single BasicAclEntry. As stated earlier and + demonstrated by corp.DomainObject:2 in the above + sample data, each domain object instance will often have multiple + BasicAclEntry[]s. + + As JdbcDaoImpl is required to return concrete + BasicAclEntry classes, it needs to know which + BasicAclEntry implementation it is to create and + populate. This is the role of the acl_class column. + JdbcDaoImpl will create the indicated class and set + its mask, recipient, + aclObjectIdentity and + aclObjectParentIdentity properties. + + As you can probably tell from the sample data, the + parent_object_identity value can either be null or + in the same format as the object_identity. If + non-null, JdbcDaoImpl will create a + NamedEntityObjectIdentity to place inside the + returned BasicAclEntry class. + + Returning to the BasicAclProvider, before it + can poll the BasicAclDao implementation it needs to + convert the domain object instance it was passed into an + AclObjectIdentity. + BasicAclProvider has a protected + AclObjectIdentity obtainIdentity(Object domainInstance) + method that is responsible for this. As a protected method, it enables + subclasses to easily override. The normal implementation checks + whether the passed domain object instance implements the + AclObjectIdentityAware interface, which is merely a + getter for an AclObjectIdentity. If the domain + object does implement this interface, that is the identity returned. + If the domain object does not implement this interface, the method + will attempt to create an AclObjectIdentity by + passing the domain object instance to the constructor of a class + defined by the + BasicAclProvider.getDefaultAclObjectIdentity() + method. By default the defined class is + NamedEntityObjectIdentity, which was described in + more detail above. Therefore, you will need to either (i) provide a + getId() method on your domain objects, (ii) + implement AclObjectIdentityAware on your domain + objects, (iii) provide an alternative + AclObjectIdentity implementation that will accept + your domain object in its constructor, or (iv) override the + obtainIdentity(Object) method. + + Once the AclObjectIdentity of the domain + object instance is determined, the BasicAclProvider + will poll the DAO to obtain its BasicAclEntry[]s. + If any of the entries returned by the DAO indicate there is a parent, + that parent will be polled, and the process will repeat until there is + no further parent. The permissions assigned to a + recipient closest to the domain object instance + will always take priority and override any inherited permissions. From + the sample data above, the following inherited permissions would + apply: + + --- Mask integer 0 = no permissions +--- Mask integer 1 = administer +--- Mask integer 2 = read +--- Mask integer 6 = read and write permissions +--- Mask integer 14 = read and write and create permissions + +--------------------------------------------------------------------- +--- *** INHERITED RIGHTS FOR DIFFERENT INSTANCES AND RECIPIENTS *** +--- INSTANCE RECIPIENT PERMISSION(S) (COMMENT #INSTANCE) +--------------------------------------------------------------------- +--- 1 ROLE_SUPERVISOR Administer +--- 2 ROLE_SUPERVISOR None (overrides parent #1) +--- marissa Read +--- 3 ROLE_SUPERVISOR Administer (from parent #1) +--- scott Read, Write, Create +--- 4 ROLE_SUPERVISOR Administer (from parent #1) +--- 5 ROLE_SUPERVISOR Administer (from parent #3) +--- scott Read, Write, Create (from parent #3) +--- 6 ROLE_SUPERVISOR Administer (from parent #3) +--- scott Administer (overrides parent #3) + + So the above explains how a domain object instance has its + AclObjectIdentity discovered, and the + BasicAclDao will be polled successively until an + array of inherited permissions is constructed for the domain object + instance. The final step is to determine the + BasicAclEntry[]s that are actually applicable to a + given Authentication object. + + As you would recall, the AclManager (and all + delegates, up to and including BasicAclProvider) + provides a method which returns only those + BasicAclEntry[]s applying to a passed + Authentication object. + BasicAclProvider delivers this functionality by + delegating the filtering operation to an + EffectiveAclsResolver implementation. The default + implementation, + GrantedAuthorityEffectiveAclsResolver, will iterate + through the BasicAclEntry[]s and include only those + where the recipient is equal to either the + Authentication's principal or + any of the Authentication's + GrantedAuthority[]s. Please refer to the JavaDocs + for more information. + + + + + + + + Figure 8: ACL Instantiation Approach + + + + The above figure explains the key relationships between objects + in the Basic ACL package. +
+
+
+ + + Other Resources + + + In addition to this reference guide, a number of other resources + exist to help you learn how to use Acegi Security. These resources are + discussed in this section. + + + + Sample Applications + + + Contacts + + Included with Acegi Security is a very simple application that + can demonstrate the basic security facilities provided by the system + (and confirm your Container Adapter is properly configured if you're + using one). + + If you build from Subversion, the Contacts sample application + includes three deployable versions: + acegi-security-sample-contacts-filter.war is + configured with the HTTP Session Authentication approach. + Acegi-security-sample-contacts-ca.war + is configured to use a Container Adapter. Finally, + acegi-security-sample-contacts-cas.war is designed + to work with a JA-SIG CAS server. If you're just wanting to see how + the sample application works, please use + acegi-security-sample-contacts-filter.war + as it does not require special configuration of your container. This + is also the artifact included in official release ZIPs. + + To deploy, simply copy the relevant WAR file from Acegi Security + distribution into your container’s webapps + directory. + + After starting your container, check the application can load. + Visit + http://localhost:8080/acegi-security-sample-contacts-filter + (or whichever URL is appropriate for your web container and the WAR + you deployed). A random contact should be displayed. Click "Refresh" + several times and you will see different contacts. The business method + that provides this random contact is not secured. + + Next, click "Debug". You will be prompted to authenticate, and a + series of usernames and passwords are suggested on that page. Simply + authenticate with any of these and view the resulting page. It should + contain a success message similar to the following: + +
+ Context on SecurityContextHolder is of type: + org.acegisecurity.context.SecurityContextImpl + + The Context implements SecurityContext. + + Authentication object is of type: + org.acegisecurity.adapters.PrincipalAcegiUserToken + + Authentication object as a String: + org.acegisecurity.adapters.PrincipalAcegiUserToken@e9a7c2: Username: + marissa; Password: [PROTECTED]; Authenticated: true; Granted + Authorities: ROLE_TELLER, ROLE_SUPERVISOR + + Authentication object holds the following granted + authorities: + + ROLE_TELLER (getAuthority(): ROLE_TELLER) + + ROLE_SUPERVISOR (getAuthority(): ROLE_SUPERVISOR) + + SUCCESS! Your [container adapter|web filter] appears to be + properly configured! +
+ + If you receive a different message, and deployed + acegi-security-sample-contacts-ca.war, check you + have properly configured your Container Adapter as described elsewhere + in this reference guide. + + Once you successfully receive the above message, return to the + sample application's home page and click "Manage". You can then try + out the application. Notice that only the contacts available to the + currently logged on user are displayed, and only users with + ROLE_SUPERVISOR are granted access to delete their + contacts. Behind the scenes, the + MethodSecurityInterceptor is securing the business + objects. If you're using + acegi-security-sample-contacts-filter.war + or acegi-security-sample-contacts-cas.war, the + FilterSecurityInterceptor is also securing the HTTP + requests. If using either of these WARs, be sure to try visiting + http://localhost:8080/contacts/secure/super, which + will demonstrate access being denied by the + FilterSecurityInterceptor. Note the sample + application enables you to modify the access control lists associated + with different contacts. Be sure to give this a try and understand how + it works by reviewing the sample application's application context XML + files. + + The Contacts sample application also include a + client directory. Inside you will find a small + application that queries the backend business objects using several + web services protocols. This demonstrates how to use Acegi Security + for authentication with Spring remoting protocols. To try this client, + ensure your servlet container is still running the Contacts sample + application, and then execute client marissa koala. + The command-line parameters respectively represent the username to + use, and the password to use. Note that you may need to edit + client.properties to use a different target + URL. + + Please note the sample application's client + does not currently support CAS. You can still give it a try, though, + if you're ambitious: try client _cas_stateless_ + YOUR-SERVICE-TICKET-ID. +
+ + + Tutorial Sample + + Whilst the Contacts + Sample is quite advanced in that it illustrates the more + powerful features of domain object access control lists and so on, + sometimes you just want to start with a nice basic example. The + tutorial sample is intended to provide this for you. + + The compiled tutorial is included in the distribution ZIP file, + ready to be deployed into your web container. Authentication is + handled by the DaoAuthenticationProvider, using the + in-memory + UserDetailsService that sources information from + the users.properties file located in the WAR's + /WEB-INF directory. The form-based authentication mechanism is used, + with the commonly-used remember-me + authentication provider used to automatically remember the login using + cookies. + + In terms of authorization, to keep things simple we've + configured the tutorial to only perform some basic web filter + authorization. We've wired two common pre-invocation access decision voters, + being the RoleVoter and + AuthenticatedVoter, such that + ROLE_* configuration attributes and + IS_AUTHENTICATED_* configuration attributes may be + used. Of course, it's extremely easy to add in other providers, with + most users probably starting with some services-layer security using + MethodSecurityInterceptor. + + We recommend you start with the tutorial sample, as the XML is + minimal and easy to follow. All of the needed filters are configured properly, and using + best practise. Most importantly, you can easily this one XML file (and + its corresponding web.xml entries) to your existing + application. Only when this basic integration is achieved do we + suggest you attempt adding in method authorization or domain object + security. + +
+ + + Community Support + + + Use JIRA for Issue Tracking + + Acegi Security uses JIRA to manage bug reports and enhancement + requests. If you find a bug, please log a report using JIRA. Do not + log it on the support forum, mailing list or by emailing the project's + developers. Such approaches are ad-hoc and we prefer to manage bugs + using a more formal process. + + If possible, in your JIRA report please provide a JUnit test + that demonstrates any incorrect behaviour. Or, better yet, provide a + patch that corrects the issue. Similarly, enhancements are welcome to + be logged in JIRA, although we only accept commit enhancement requests + if you include corresponding unit tests. This is necessary to ensure + project test coverage is adequately maintained. + + You can access JIRA at . + + + + Becoming Involved + + We welcome you to become involved in Acegi Security project. + There are many ways of contributing, including reading the mailing + list and responding to questions from other people, writing new code, + improving existing code, assisting with documentation, developing + samples or tutorials, or simply making suggestions. + + Please read our project policies web page that is available on + Acegi Security home page. This explains the path to become a + committer, and the administration approaches we use within the + project. + + + + Further Information + + Questions and comments on Acegi Security are welcome. Please use + the Spring Community Forum web site at for all support + issues. Remember to use JIRA for bug reports, as explained above. + Everyone is also welcome to join the Acegisecurity-developer mailing + list and participate in design discussions. It's also a good way of + finding out what's happening with regard to release timing, and the + traffic volume is quite light. Finally, our project home page (where + you can obtain the latest release of the project and convenient links + to Subversion, JIRA, mailing lists, forums etc) is at . + + +
+
\ No newline at end of file diff --git a/src/docbook/resources/images/ACLSecurity.gif b/src/docbook/resources/images/ACLSecurity.gif new file mode 100644 index 0000000000000000000000000000000000000000..7a0d0aa82345ef28853939032b357a0934c5940e GIT binary patch literal 4041 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 literal 0 HcmV?d00001 diff --git a/src/docbook/resources/images/AfterInvocation.gif b/src/docbook/resources/images/AfterInvocation.gif new file mode 100644 index 0000000000000000000000000000000000000000..4a27abd650d9b3456912288217155cb646dceb30 GIT binary patch literal 4672 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 Co>9O6 literal 0 HcmV?d00001 diff --git a/src/docbook/resources/images/Authentication.gif b/src/docbook/resources/images/Authentication.gif new file mode 100644 index 0000000000000000000000000000000000000000..2111d2e380459ec67831de101affc01a59297b50 GIT binary patch literal 12955 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℘n5yFvogccF(VEnWz9w*(jqm|BLz|i71CB&QcAJX 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 literal 0 HcmV?d00001 diff --git a/src/docbook/resources/images/Context.gif b/src/docbook/resources/images/Context.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d63b197ffdb0c80e3f1d275bae0f7757bd1b748 GIT binary patch literal 4269 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 b&UT4;yoY!*n*zd__Q|n3M=ep1J_!6jzF*>! literal 0 HcmV?d00001 diff --git a/src/docbook/resources/images/SecurityInterception.gif b/src/docbook/resources/images/SecurityInterception.gif new file mode 100644 index 0000000000000000000000000000000000000000..a9e27c59cb7ba2168405a30527331e898401f3c1 GIT binary patch literal 5532 zcmV;N6=Uj0Nk%w1VHW~50kZ%A@9*yb007(D+veux|NsBU$jG#`w9(PgwY9aqy}h%u zv#YDCs;a7@qN1#M)j$~<` zXsWJk>%MR-&vb3yc&_h!@BhG{a7Zi~kI1BQ$!t2GP-tLEty-_xtai)odcR=7zz8mz z&*-#zT^8%u@VI<_c2@#~ynfH``~QG}f`f#GgaHKtWQL55j*pO$l7)zhIe3$qnwy-4 zl@^$tqNAb#1_z6zs;jJ|rl^;#va_qNptQKT1OW)JM4`ID!nM6`!^g;dyuK5_$%~;=>Y5P)bBv?^7o_kGxqrX;>CCTY18L} zpTL8T2*xmoP~JO+o7NeG7)&7#ipV4m!g$Z(3y!t_{!B#pFdrXG8m~ce267|7U)EUW zNeQUsu9Goe-mn=-=1o8RZ2HVqlG7A%p->*(wR9;{Dl?rrWs0<_QJq3~mI5X6xhQ)&#k^JBxG16vj>OYh#cp#jeoz53)_ymb*X%Q_GBWWTKi=a!ok zb+WWAG*hzexk_QWEMHHZS~{_C(2j45F7A2s^RSqIT+bw#I%w&~DT_y&UB>o}hryFh z&7SZ2S*E!`{yJZ3+4D8a_rPy5G=2Z}A;n++Df+=MpgZ%u6`6APRTE!B!vKfFfe@Y} z;R+O1$c=LvUN)Oz9fs&hfggz|(1Rv6_hCILwg?o7YPr}EhcdzkV>UIqc%cdz<~Rw8 zK6bKW4Lt^_%aAA(X=Fk)E}74b6FMm(k|!uBB_&N>Dcdw!HbkWfR(5$!mSRfsX4l&47z80#*GKj ze>J^V-A2q^h3G{Yy|x)poK4ASk_c96sY<6RGoGoV<@q63->np0sH}dPs+ptilToXc zmJ?u$fsTL?9`Q_fNU5Im290U3(xqGfTTmrs7hIGQdz7+oS=tqDvB5S|uIBAp+;Y!a zJ1ug{*@vySM}k|zu)4VwZMt%ewJx>Sp39)VO{Lr0x|!zpDs=66$C|+iv*)aO4F9v~ z4vz8`U3J77R&jUhHGHdz#@MRy$Cnmd@Wts#nlPWYSjO;f$9)B|$i~7vYoy;k+;N1I zt-RV_BrBM6Q>p=dOt2zQB$muiy)0nDJm2(h%R;-n>Y*u%r}4xJWBjsuNOSElmla{n zHLN6W9dg7q-;5s9CU?#DV?CSw8Lt9Y?bXvi18i+_TUQ(#YxLIp?9$lTj3YXg<>t4< z?J0h@;K+HXHmcF}JGbLh>1{dxzy@PZGlMZx9T2i`5*?hPbLV6@3uqq{dYpD2`>4AH zqRy^^v_1v+qn21vvk;`hJ`TJVAxRA2-xL4g4X-~b>Dp$JDv!V;SBgeXj*3RlR& z6zI45T3CI7lg!agKxhqc;l4$VM*Gky->~85xO4G9nU^n2aMEFPX_V zJaUqqv}7koSw}c>l9WXHBpoxkNmRPhjHZ0$B{pzMLAFwsxcnk0a+yjE)UuDUtPq%oJ7gk&=Fna6~tG^a^PYFhJ}X~bruQ0Yug4*d3?CH~@8nq2b9G&*WA;Buev!-Nt-ct9{RHaUJ7w&T^ zJE8gu-CgxZS@p(O)3#EslEbTGT?uA{ry=TX)s})vYfsjiR=2LxtQM84T35u@3b_@p za2;k}yF`z{stJ3@6e>DZ`A)|AQLoXUtFxjpwf9`-bwKfyCwAeyJ17>4n-rxQn+T84 zdc&E6rAe&-MOSJ4NF=Hv-R}sBey6?KCbe&^*mC^Kt(yyZR|;W&6v9C zvp2XU4PXEN+F5j|m90p9gJcxcN_ZtK}Q?ax7(* zbK2YdMqhbN2D=Dz?Sfiku91cHqkt8bZhafZ?@H3Vv~Z8yv^FzZsiLqJGfuO{#w`Hl zi!@@9)@|e)FPvz%zJB>HIfE(3Te@+8}t+cSZ9U; zVjHJ!FpDcrXI)34>b1*9)47zDqS2Yt`kk9_sug$?YXv+CA zx9qx0WipnJw-hCbN156LMJ~NEo^eGa>xz)fW5~Hpq=AntULz-&&5(s%?2IxX!;<*| z0ancafI-RIW|)}Bfp!O-uX<=ia}&jWPP8ZQ6zLHqcdQCo^KC)d-Nkac(}$S!RP|A0 z(e}B#jy`Rv1)M!pyX4bfEv#;PyGjC+TGI&)sbQ}KVNhqM)x1`6Y5nYIV0RRa!nSp) zaZPFc1k=}z{O4D+q#vHbk!d-3K^61?0 zRyXm)YHWLFbjh{bti`}x=i|0*XzbRWyJwvwG;Kz|1n*C>&gCjZ*A>qUk7kJ;ez*8$ zZp#(!*1Q(CTrR`dUy%FNx~aWtjGH>+2j2MFpbQ=y(_CI07Ta*Ww(n+K+}|39IaDYA zb!9l`H)L-HS4K4#;)! zT+Z&2-MDZLW}d4u$iF^lg}eM+WY4$tFpILf@BQv0*8AQUL+_T0ou+yulD?H^Rm1zT z?3jc+tuwQDktF=@a#Ex^aEn)YJE`#P#tL~aqF5&X((_9Kz1?9k`hp16qgCK%V7ul{I*m4eRnN8_|d`pz+tw{`TK*gY*n7_#rueCaClU1IERlldnZvpOm=t@)t83>g@X7;e&=_HX~;x> z2t0yFP@;xr7kFm9w}^$)hz3Q7d=_2~Xns&ciDYGoQMhRN_lR-ji320!a6=y>XIUYsELe75CbMC90W@0IG|NaB_%$+ELwnx%jUrKW z5LYfi<||24kKVyy(wJdZBVlM4FTLoB?-)}wVJ;WeV>^~OQKpZVf{x^v8a#G6w{nUD z$x`-+cFT2;`vMzsSB`O)WmxuR9NA@HA(HM`k!JXN#aM9MA&@9pR=`qdTk?`usFFTH zku-@>Hkl(hnUhNBiBRX0LKufc=~ICylo>Y#6y$yu)Idz>Ku-DpKk$c?5Ev~%6o5iR zELgciS}8*VNR^*OGrM4QF-R<6nUVqHg-J0~VR4p8C}(47b~C}2bLo~tc@1*emM|d> zc6pZ~k(YW&8z<41KV569R zf|r@edzKWJ%>-_90-B-8aHgb#V6Y!wkeaHge8j|suvwK4MVqymlmHh=xfz?gNrt_d zjAvwpVi=t9Hk`O=oUaFM$*G(;Rh-Vpo6h;1_avRuIaAO{NY;6s1X)VW$(q(a zkbkWv=BKVpEbLhhZ0{k6m^OK$KL3X`~=(PW+^#2ibKJd2>)ErD}RG z5Q%fq;iVu1rgkZ&V>)$+Rsc~dk8fjiZHjbq!#GDHjunOyURnSJK&NMEr*~Qp^^|)c zrgLlRWLno@m8y_$ieY}y3p%uzjM}K$@u*6gH*WX;GlZjAYUdw=12ydhjVLD>+u}6p zBBvdiho1_nu7?2v@IgCSBd@Tl9ORa_`f~q?0VtZH`gsb++LyyhtOiP}9 zi?QESv12%}9D7Y3Yq8;qQXw02DhRUhcCsV;u^Vf$A=|PpTe2zpfPDCbR4KDF`>YWw zf8&Q_;>U?#sk0=@vj?kyqqTt<*oXT{Q3!kgX$hEYOT>VPNVIR|v<-W%qS#8LSf|z` zu@T#aCD?4WX|*|}vbh9<xLF&vipx6WT9k@PgO!`PRiLl$qPd=XxvBU&`$;OG zhKG%kYo<%FqI;B+2x_d0R_*q>Un{zu)vN4aSGF4t-5N-_TXCl=b=6aETSdDa+Pk`o zamL#`wF+swV>-#Z4Y(V2!`r;I`*W=5cvzUK*U+M}Bcrn@D<@ICdjTDqWsBZRj4RoV zje@L|wW5i`y=!y6bkh^zyS!|sqr=7jD!ns|-)oDE2qy45b7{&Q{ENQA_`g-+arql@ zRhqtZvmGpPt%;|zD%69XZFQ1cM3Ie6Jk&T&y0AC8gPva?p6K2}dVql?N|0rE1 z=Pf0-Gn_TT35=!LXu=JW!e^mnEDWbEJdz@ZH`3*$0l>KgyDe9P!j+1Uf7&&sphKXG zOgf5>Yg#UnYBo!Zrc)q5bDAWh%EW=H#9G|MOH8X#Ot2I@F3?yxYwR{3gE@jaE&_z8 zXDop-Y++u9i_f)?4fDp*7{`q>$BCLG6ii>K%5#27sp{A{fBZNwbEt!yifJq|?Px6L zcry9eISSCJ?^4JE_hW?Wri#q}j!1LGFNzzR3_^`E#h{F)t$cGzlgTi{sjA$u_^YZS ziNkRw9ikkPPnyFUc`aNFbU2j8&X~g^smp`|!~59Fh3cxn{42uD$yJhjr228IT3sj? z#Js$AxctkfD$QG*L$&NpyZU<>G7$!l0J~ZO9@KrbXd$Js5$oK}C~(dsG0)d<3cwmp zD2jSLG7$)nsCBFX$EsT35YR3`&?)fGt^*(7rmQH6g~WSm!W+EUdrr?w6yhtr7#(`{ zn@$z|yB=L@AiY(dD}AyHS}C2mD_vqOJ+3b;O~S{n_~WiN9YCo|0yUk}`GeCwjX?0K z0zIA6KpoT&wA0>HulG9tLORscEo8(Xz|>GpLQh@QJ0#U2K-Kqp)mS}5TD_cu7;Gnt zvi~NxWer^VG`ZPS)@;2KWS!P>J=b(y*LHo^c%9dfdw^)|)`Z*FEc@4?nAUH-d4L_W zg6)WMUD&-e*n0igkgb2c$F$jsvq7lVLulFjMA78KFaC`& zeFb4ng_?WgN>t-Wm6Rl*)KXv}Kpq7fy%9qWizG4ArD+ZVnpeQ0 z1gO37oQo5w=O!&>5>dVnYLQisPz5yfjgo%#qw(V*IBz zr*w!=(2hxqew@Kb_lXvz!DB2lzuq|uP{hJckfEN)SqG^5sEz@^&UN0SnB1qZUgyfb z$61_X56N`?!2mcEtfe>YmR#**jO_|==sOWxfojS>roPF}b)25X!zFh4Xp@^CUm-SU&U`4)iZy zZXT|-t0Tq%0q_F=78e&SEY7sHK0S82 zp1#7=&aSMtulDdBn}*y%lahkGd~MUyJNNRF)Qptw zbYAZ1nFRdf;duUyTUUdE>gVR3ZcNyDx-_%rc6q(%qFT(p$B%A6LBY!LaUZ8T?)KCl zD=M~gH$Q!PaQxYLWqmm@I_~S&dC1CRXz0!EQ&o_WbKA8u|GXS2I(zoZhwjkGjb|@Z z?mLnR#vXb-(b6*@0fTrMsor>bTyAcU%ARz!kQjqKsj&4%Xw&XVX-`IV&86n@C7NoD zt=B-`sNsA42C@6=E|qQCbV)6F&)|c{%~vXPjP$;KmAP(?o1b5(uF1Z6tKQDh=J|^* zw*%=URW(Hof(0iQ3{7}2Q2O9u=l9v68@H;`j~|1g_g^gCYi)gY{^zXqfz-tPhcfbu zO4_d)TiKX8IDt++sTGaK3$d|>BT*;IfBg7WRd=fO;$AS4cj1C$m8FsO;heUbOvy;Q z!qzRq>J!Xu3GLUaq|(o&l~*(pvpm+xo2@Y#>#GIBQcq-WPR%}@UEj<-Qbgb#e)gij zs=fuH$X~zRMcu)NLL+&xYh|Cu78ZU}!*{BL@BH}bZH>6|%+2nZkKfcoLatn|S21*{ zyHrFAOITP~n7Ch$uF<+CIyro=!+b~Dlc(L9?k;Dq)Qj3KL)-T(v3ERQQ%a56_51gH zdvlI~Q`n6w7wn_A&3t;XC6q;uPs?v=Bt-33@{LTnARK=(ajgZ4FN@bGvi3yxJR|w#GcD-#pZQ>Dg7@tHE-wt4%L%I$WB+@wMV}NkHV9#;@O~ zD+70>Wgib6E^#`wLdlx?3mBF8Z&~WV&@GOCm8-IWXFXNTdg|<(3nfc7 z*8O+Er`&6ODdQE>D^O>&xoo*dtNM!%!pEzY)U~+xn^>52NywJ3l`b979cTQ~qU=$h zyx66+`1yK1?rpI3pEI${?&oZB5u@nZfA}_q?e}cU)$e@0SF79H!3AI}N7A#ybHU$T z=MGVRNaN{hKC1XV=p|M2{!HJ$>;F9B46Nl}*@ft8dg~798!0&F8C|zZK5Eor>o>Zi z%={|RFft)HAX*!r0Rnb8Vh!`>M|ejQDSuXZY0;$1$b|Jh{?77Bysh23&Ve}w-qf! z{A%qh_ZTZJB}W{q{02}xYbrRzQ7Dr+tEtmgbrJs<1%+!`Eo2;75owEhSFC(pC$YO{ zxhST_)-10?6=U_u51GaYLrhZtCPG0-e;b{W+|!&RPvuHc#clU4vnXL2!5 z)z)Y>l4CfhE+!(UX1fsx)nzPyOBN2I^+bW0{`>Y<1#3U+eq*=98euMSWL`i`fx^7M zn_ab^5P~%Og&7<@M92o8zHZ3HPe#LRIn`(KVyMDXxE|OWrHC#0NNqu_?`m7cX}YIk z`EX9y1$S1H(|=IcIuVLMTWz1yIrUKi7!r=K_^7k)BX;~2 z!+Nhdy8#72GWR5+ATe;CNgB|QTg0PM{sTK3Cjq%Df&sAIiE3;Z^kBThF_XC2E9snW7e)wZcJ%(67;I~_Ti}OuMwX7Dk5UM zgQT!4?*wjpZJ)e@q#e%!^afK6DB65L&5o;dOrmU&e5TPhM+~mD;4JyRQQrJ?v{H;f zTV)IdIr(4pJ{Vo3^oSP$^CBif6B({6syM>XM)wn$Af-s>-hsVGaOvlp9%V9cD0qYICOKIiH3YT3jO{n_u1U(ldgx!WHS@O_VW zZ-yhDkg_}>H21 z%D3e@Bu^Eo@bSv35o`20psSDkvDS5!+J}{#mB*N4=_ndM`Q;IG8jp?R9&daNp=!N; zSa)<~8Z>}%&r9r*m9aeK_^s(C*0FUz#H1yqZ3QsHjt?qyg*?#)Hv&Z%Q&nmuDC& z=mhl2pay~b6y;Muraxgo*C;H=+^W`Qf?1ZO^F5YbsDo+&0Yx&_v;}^jR zy(f>--ej&y8||>Y-p$J5Y|*k`))y{(;;dpd%f$G$h@LZz&g<>| z&|9{N6BfptCgw6TKo>58hOu$m=gb8SBUo6%OMe_cqo`gAn7fRDCW|bfoi?^@%kfHS zW~B}L9~w~Fuz&a)9R+Wzj|aM?Fn{;9p}iF^2x!S;bG2?pQW$QNqzk~OUV9iDk$!Qg z6Mw&opiXBI!rgyKj=AnOQx$|!qgwFGq}?=4an@%RrbIJ;9kEdsKfdiT-M1g5?{wRB zcJ%C{=XdSJ2iIK-fgMFoqtYA;jn&2C$WbNLtd`2z-HO5IR0^&Um|-ZSa7{|4#Q-*D z5g5vNij0Q;S8=qAqAYrVud0Jcp%RjbC5BrTJkOre47Ps_dIf;U@44XI3F>Hv9zuO z@CycUF6c0ohde3r2)qE8GDt*}SSfT;cqx}*Xcp9sP5PVAV;Br#YRV5_Lp$_&E59v^ zkLx^eyCu{Fr1Wes$cTo_q<||Xm(KuI7;u~fyd?qKWaOg~xe6{=E5;?s2pdq~90ok+ z0-J;oOAHJ$4mYHLtVNXK?~nW{!X3o$-6-XWrTnoy!4r(Q6dQ0Y5At2N%TNHXv7~4I zNE!SQ!I12@JDGWZBSRpFsC=v-<7G{x0AwDeOConvK#b#1!WghW1HQ!}S2M_=3|O8+ zF_Pkw-EnaY-yD>fy%(f%V;YJnAw0mUi?EM}_;4W&nOxjMM)ddPdNTO8YxFy-UGq~Z zaap?}zsJqvk8#}@9lB=5z^)Y$SpvRJ(i^Z#c1#PvZDGJC#JGGJ*_;7a%gDdEU_XcQ zT8dA?AR_^>RzPF}mhE?eC7h^lDTi%Xlw=m+Er3rG)0SVw$%D2WBB1&zzDgDmvlz5% zHnpI>cGv2Sz<1k|b4F%IF9x4)hx|_~0IQK7d*FjSiaCH20%Q*Jl#odh0)?R*at#A_ zhKoBRART7Fqy0#YjI0sjS0pA=1vobmE-O0G4In!4=q^0?8$-@n2%Y=JScczWy?htU z-fgijZq3DLo1>Xc`1ltVMOUL4+jvDDM-?=L>Q_X#T~e@td+IFf)CumXI^PpQ zucsJmIpH~$^v06%kxcg7GP4P+qOr!FUrp0vZvE6+;_$RQ+pS2j{LBhIJ)cb(WD#y+ z1T6s~I|G%kK6O@hZq|cvSBxlgKq;s24X1QcQhIp+xhx~d3rO)8oalkL0Qe1LyqARJ z&I7kffJ!m>;9W)rx-I*SjsLXqiYdp*b@5hD!@Tt3*lO{kS1%NVFkT+5d9_+W#3BcA zfk6R<%mt4!PuZ4M+L6hh&mg-|Lf@hg$L4@v0r~?bog@Gzne?Xl(i$f5o-NIX2Y8`= z=Y{YlZgq$(F@}SCA%x6TjVrzR)L#r;8;o}`$;+n%jl-s!d~d7@;kA!BQB zQ3iJ#QmoL;!P%9ZQ{kNA2#~SRTG_kG+&E$=KtF_0*({tlr|xtvRQ$7)gh6dG!a*jq zi9y=WLpIz%xH3`{i%@`qjlu)8m#7{*E`QaqD^*nfb>rLCvbafgK@6hPX~RkmI28a<5hI_l4s!u%UJB|8pDG}+nk#ObG+;> zK$ZfvTxvx8shk@mEBi~w;z&kPdYm!c(Y|Vw(nONd15sRmLm?X@u3*|}Ge9pHsSy2Z zgourhATPX4u2^>L7*0h%{qhUvsNUrmN-gptByq{-<4Kd^_HjTQz^$9)-ooW|v@uEj zO#BK?z`~>P?T2b)06vwHUcnXl-SHcVpc)72)X;61$Obp3{R*CVR&s!5F2Ni4z;sZlE2x z`>8hZ9p)Hy$e83XbQf6ku6p&J{=~SqTKZQ4_(mSxLcgz&B^S@gw_y?Xvw(vkJ)<&e z90V6h2pc3+@8_Tcla?i_Ek4$9LsZ+xq53m$Ynb#C3^;72BXJWLz`22#^!`F|MgTnr zfd4%}FJ%Ey91vjXHq$Co3l={k+T#aipUP)9!qIYg|zPmic@_@Pm)lCGF;5rp+1y8Bf4%nd|g@E zYVI)HKZK++z&Z&&hKFQ~6QWphrq2m3JVYsBBm`ABY!#Y;1Nura{a>+%TEkY{vP@a@ z+-xY>nY%i~B(WmKPQlZSQY1x2P}&yp=o2n+aTtEW#A!($YRPb~r39v|G*k*t{Jb;H z!fkQ5UywiwlR|-195#VG;`-lZ>u2rCA7ZNnFxy0kB3&kV@eM%J9gh zy5be|d~dNgTK7OoqD!DLXM7??JNK832PQZY+=YaS8F|WSbGfOp_6>j|5WklRrjI<+ zwT3>5pHvzEwzqQ2h`=A_R!38XZZ5@4jK3*{e+`dMTzb*BD8&z|l9M=OITATkh^t^A z=Pv!j7Pcz0>cY6Vd@gud+OJYB_Zy?0!;nM_D00PL;(%gNwHsQ!UpnBctWb>yHca6F z-ji#Q@y5bbi`Z=vR?P&wIb7)YN3xSw1IhuQ`d|JRJ055$XmBa|7%(T1^XJhl#c&=9 zk1?M5%W(KJL@fa^AAl^Ru!WQ`E`=TAABEhKBMC@xLXxUL&fNP7%Iy_ABoig%Qj8>) zkfX$ug!LnjHEYz9x=kk1k<= zEAPSi7^M*-2sXbO;|)3d&G(C+2HI z5>b4y^RtwY5qDrpEg}iPl9KT$o2}`?IFsLdlipcF;*AgPecSviI%Vki)Pj-14X?kD zxGo;8*9YDp#6RMK7Lu76>C7Pp`F_*%+E0WTj3yPdk26W18Qq~`+}Rs-T&y%uFhOSl zpxISe0+k2}*U_<*cLVO&hz|e3%Iyr5W#-vefK7Kx45tdD15fA;aY#hD_DMK208p`e zsZ((iF9m#sTn@)9^(L^WiPU>V6qOkRRM!{sCH0)msA!XBShciiH-Y#1j$9}pWd zuC0-2ygy3ncbt#}Q^a9ZIfbeOSlNK;FUtOn5gNJFv^8-2N?M+9_K=v^&%`msl5;eu zs{F^kE#xrK9c9*6McOCr7$?FRs;gxIMk9=jKev&d~6? z?tvZ7dHzP4A)S$pTc8e{{rD-Rs(;Fc-ZN_-yfvj1hUKmdV>%(P3&-DioKwAjs7DU8 zrl2d%=vjJ@KKL!)I zBxPn^V&^%af5rd=nT){`J|d4ALzuz10<1HZiK`*oD5xUr0B4~8aWg0g>tO;^PBYa-fO;KJRVK?>++FM^qCLs1mb;`qPGn&nDzif3~qaFzT0 zBME~*ROs$5DdI)}^zqJkMusBJ|p>`MQq|3dmv zqwnL+v_Pj2rOY)yo@b~V|4i1$FC&g^<(SsWK+v-tjm`7PA2)JWwbT7lsnIKWv;v&r z(lA28&rUCYP=JQlOt%$3GV{tDI7VI7&rV;}<$x$0YJKz&gNS1LwT(~MCdv3p$-n?xxasjUvf{`fAjjn zX_v|RL&3nZ#9mS>K$LBM^tO6S+~Uu7wQFJ47EWR*fUzvx$KwFk1SxQkbGK8!o^#*dhwITSE+gi>lF;r8Zf-kgkOg>lo z-1mtUY&t$B*5_0r!d>Uq5K7`J!xgw^MWAG=dUWJ*TD7@42LtsOvhL&F2)p5v(ND0` zEXL1l_9;@!uJ&c?8o5yt7#yLZqk{~iIxZa5FMxMm=Voj5KI0dEF7kS?BhnM>6Z&0slVSM&GvJe(-i8JDm(!K#^~SCNL+X- z1~>$qSejSKRTCC?@N6NiFOuwKI(LzstR7v6nd7WE*$D`R)Vx>WhW&gM-)}vdX(L&N zvzeNnC!5YKwv zrRc)}6!OG)XFER4K!_+b@iLdBe}*(j|5D!qS>DS6&I}=9tzizNSArqmWId*ud~)O> zCYJZgNnaW~MKGIYl9?j(cw95Z{2@%xXYz^TnAhghC`jPqSaQH^Swx(B@lC*?WZoC6 z`3yISJ10^r0K?QSu&acI0ttB{c*7A8Z_lmLyZhtTtd`K-sw_5SgnVW@)fw-^1er&eM_dIc7{h=y-bc9S zw}A3wCg1pidESH+tuI245ZzJYhKFCt;UCjIGtc-IM0GBn-(H=t&98+{9@o|0naWnS zm8XR!JXZd}{OH)lg>0$aOGb8sLCF{lTMH=#!4Z*5h8~%veLm2s|1a+Owh$@wgB}}< zm{PeJ>&Caj-KyPMk7ub{#L$hDIV|*W=kfFtQtgd`ZZ+-M95cp+=HrR_luhI{;WDQz z7A@(3@h?!VryE!%>P${#$Svrm$j_#Bt{j$avAE_$w-$A(b;(}b&3ajUC&6TmhT57X z+EU=mt{s^}c6q5LUBGr0AIv}SJY;wh2cLF6`Yt|0p?HqknA)>dZCL7BRopPAxl|r1 z^f24t2N=w{(Y0kq6otMJceqnWUwA?>4_=|TC>#~gPJ>|7$iQl&FNGj*)KER4&u7hX zrCT}YogJj0+8kRh+7pBhj3e>UU-fJkh^y1C-u&m(SGDOzDa$Ce*?bD8(?wWA`q%bk z{SA@_g53e4^axo~1LRM8PLKjW8sH~Ky=H{`+Mu6Cn>1;_TqVCu>8{~g9?4sUmvg2` zj|R8%Ni!YMb~h*P?fRZncJtY!vBY|m{HZI^fN)ziVD5yU-|o)6GjjWmOA$4}lKNMz z7|C0@M>4KfZ~!__Nc;3uu^(L@-j*2q?-38c_8&IM0gu=jU*__J;d;F5-Zf@-|2BcZhT9@WjG~P7@FZiuPh(SG{3g7{=8og; zKS5$MZui22cbq4^Ptqq9mxpRWrAfc;@@qj+cap}!%VO*4?s?6?Umx=hLHaFz*bvP8 zUU}k2NFS465ypRVjX5?Q2UT{+ND*n(HeG->VY=%8CyV{MD!lE^%qRYPl-g$k)?N|6U`X~1vq$?Pvmj4`iPf=U=F?Va( zPw`DGjbx9xavgeiWxDeCuC^IR;@5=DcFc(~NYh|731~Y@4rE)MECFb?d~GJKc$aFc z^HD|z3aBEa)N-oo(QCi*{7;esV{fsE?zw zl6p#Nhl5p3uPjOJ1|7sUY0e;345MP!bXLF|zBRO+By%`1$JbQl8#1smNilVvSzAW}G~>Es!IejKOV;f3A`=1o{{gy1tX}{C literal 0 HcmV?d00001 diff --git a/src/docbook/resources/images/logo.psd b/src/docbook/resources/images/logo.psd new file mode 100644 index 0000000000000000000000000000000000000000..e02dca01d5c830e6823df9f30de5ce1b3d34cbf6 GIT binary patch literal 149793 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