站内搜索: 请输入搜索关键词
当前页面: 图书首页 > Professional Java Development with the Spring Framework

Acegi Security Fundamentals - Professional Java Development with the Spring Framework

Previous Page                                                        [ directory ]
Next Page

Acegi Security Fundamentals

Let's now get to grips with the Acegi Security solution.

Authentication

The major interfaces and implementations of Acegi Security's authentication services are provided in Figure 10-1. As shown, the central interface is Authentication, which contains the identity of the principal, its credentials, and the GrantedAuthoritys the principal has obtained. GrantedAuthority is an interface and implementations can have any meaning appropriate to the application. The GrantedAuthorityImpl is typically used, as it stores a String representation of an authority the principal has been granted.

Image from book
Figure 10-1

As shown in the figure, the AuthenticationManager is the central interface responsible for processing an Authentication "request" object and determining if the presented principal and credentials are valid. If they are valid, the AuthenticationManager populates the Authentication with the GrantedAuthoritys that apply. The main implementation of AuthenticationManager is knownas ProviderManager, which is responsible for polling a list of AuthenticationProviders.

There are many AuthenticationProviders, each devoted to processing a particular Authentication concrete implementation. The implementation supported by different providers is indicated in Figure 10-1. The ProviderManager uses the first AuthenticationProvider capable of processing a given Authentication request object. It should be noted that because of space constraints, not every AuthenticationProvider included with Acegi Security is shown in the figure.

Most of the time the DaoAuthenticationProvider is used to process an authentication request. Figure 10-2 provides a class diagram for this important provider. As shown, DaoAuthenticationProvider will retrieve a UserDetails from an AuthenticationDao. Most developers write their own AuthenticationDao (to use Hibernate or their preferred persistence strategy), although Acegi Security also ships with a production-quality JdbcDaoImpl. DaoAuthenticationProvider also provides other useful features such as caching layer integration and decoding of encoded passwords (for example, SHA or MD5 with salts).

Image from book
Figure 10-2

Although the preceding text explains how an authentication request is processed into a populated Authentication object, we have not covered how the user interacts with Acegi Security to actually request authentication. This is the role of various authentication mechanisms, as shown in the following table.

Module

What It Does

Why and When to Use It

BasicProcessingFilter

Processes BASIC authentication requests in accordance with RFC 1945.

— To publish secured web services — If a simple single sign on approach is desired

— Should a simple authen- tication dialog be desired in the web browser rather than a login form

CasProcessingFilter

Processes Yale University's Central Authentication Service (CAS) tickets

— If your organization already has a CAS server

— If you are looking for a comprehensive open source single sign-on solution

AuthenticationProcessingFilter

Processes an HTTP FORM POST similar to the Servlet Specification's j_security_check

— When single sign-on is not a requirement

— Should your web application prefer a form-based login

Each authentication mechanism has two classes. The [Strategy]ProcessingFilter is used to process an authentication request as described in the previous table. The[Strategy]ProcessingFilterEntryPoint implements AuthenticationEntryPoint and is used to cause a web browser to commence the specified authentication mechanism, generally by sending an HTTP redirect message or some browser status code. Each web application also defines a SecurityEnforcementFilter, which is used to catch various Acegi Security exceptions and delegate to the AuthenticationEntryPoint or return a 403 (access denied) as appropriate.

Acegi Security supports multiple authentication mechanisms at the same time. For example, it is possible to handle authentication requests received both from a web form (with AuthenticationProcessingFilter) and from a Web Services client (with BasicProcessingFilter).

Client-server–rich clients are also fully supported. In the client-side application context a RemoteAuthenticationManager is configured. This RemoteAuthenticationManager receives Authentication request objects and passes the contained username and password to a corresponding server-side web service. The server-side web service then builds a new Authentication request object containing the passed username and password, before passing it to a server-side AuthenticationManager. If successful, a list of GrantedAuthoritys is passed back to the RemoteAuthenticationManager. This allows the rich client to make authorization decisions such as the visibility of GUI actions. The rich client will also typically set each remoting proxy factory with the validated username and password.

The authors of Acegi Security recognize that many existing web applications would ideally be modified to leverage Acegi Security's authentication, ACL, and services layer authorization services, yet these apps have an existing investment in taglibs and other web-layer code that depends on the Servlet

Specification's isUserInRole(), getUserPrincipal(), and getRemoteUser() methods. Acegi Security provides two solutions:

  • HttpServletRequestWrapper: Recall that Acegi Security's ContextHolder stores an Authentication, which is an extension of the Principal interface. As such, ContextHolderAwareRequestWrapper implements the Servlet Specification methods by delegation to these Acegi Security objects instead of the web server authentication system. The replacement wrapper is enabled simply by adding the ContextHolderAwareRequestFilter to web.xml.

  • Container adapters: Although use is discouraged in all but very special circumstances, Acegi Security also provides several implementations of proprietary web or application server authentication interfaces. These allow the server to delegate an authentication decision to an Acegi Security AuthenticationManager, which returns an Authentication object that is in turn used by both the server and Acegi Security authorization classes. This may be useful if refactoring a large application that uses EJBs to a pure Spring implementation, as the existing EJBs (secured by the application server) can be used in parallel with new Spring POJOs (secured by Acegi Security).

Although ContextHolderAwareRequestWrapper is commonly used, container adapters are of very limited usefulness in web servers given that Acegi Security provides a far more extensive and flexible implementation of security functionality than offered by the Servlet Specification. One container adapter for which use is encouraged is CasPasswordHandler, which allows an AuthenticationManager to be used by an enterprise's CAS server. At the time of this writing CAS does not provide any usable implementations of its PasswordHandler interface, so this container adapter makes deployment of CAS much easier.

Important 

You do not need to "throw away" your existing investment in code and helper classes to move to Acegi Security. Integration hooks have been carefully considered so that you can migrate over time, leveraging the benefit of Acegi Security's additional features and container portability without major refactoring.

Storing the Authentication Object

All major Acegi Security classes, and many user-specific classes, need to access the currently logged on user. More specifically, they need access to the populated Authentication object that applies to the current principal. Acegi Security ensures any interested class can access the Authentication via the ContextHolder, which is a ThreadLocal object usually used to hold a SecureContext. The SecureContext provides a getter and setter for the Authentication object, as shown in Figure 10-3.

Image from book
Figure 10-3

A ContextHolder must be set with the correct Authentication for the duration of a given principal's request. For a unit test, that duration is typically a single test case. For a rich client, that duration is typically an entire application execution period. For a web application or web service, that duration is typically a single HTTP request. The ContextHolder needs to be made null at the end of a given principal's request; otherwise other principals might reuse the Thread, which would compromise security.

Note 

For JUnit tests, the setUp() and tearDown() methods are often used to configure the ContextHolder with an appropriate Authentication object. This is of course assuming security actually needs to be tested. Normally, the POJOs making up Spring applications should be unit tested in isolation without transaction, security, and other AOP concerns.

In the case of rich clients, the developer is responsible for setting up the ContextHolder for the request duration. With Spring Rich, classes supporting the use of Acegi Security are included in the org.springframework.richclient.security package. This package contains a LoginCommand and LogoutCommand, which hide ContextHolder management from developers.

For web applications and web services, a Filter is used to address the ContextHolder requirement. A series of authentication integration filters is provided with Acegi Security, as detailed in the following table.

Module

What It Does

Why and When to Use It

HttpSessionIntegrationFilter

Uses the HttpSession to store the Authentication between requests

—Because container adapters are not recommended, this authentication integration filter is used in almost every situation.

HttpSession is used to store the Authentication between requests.

—It provides a portable and flexible approach that does not depend on container adapters.

HttpRequestIntegrationFilter

Obtains the Authentication from HttpServletRequest.getUserPrincipal(), but cannot write the Authentication back to this location at the end of a request

—Mandatory if using most container adapters, as this is the only loca- tion the Authentication produced by most container adapters is available from.

—The container is responsible for internally storing the Authentication between requests.

JbossIntegrationFilter

Obtains the Authentication from Jboss’s java:comp/env/security/subject JNDI location, but cannot write the Authentication back to this location at the end of a request

—Mandatory if using the JBoss container adapter.

—The container is responsible for internally storing the Authentication between requests.

—Do not use the JBoss container adapter if one of the standard web application authentication mechanisms (see the previous table) would suffice.

Authorization

Armed with a solid understanding of how a principal is authenticated and the resulting Authentication is stored between requests and made available through the ContextHolder, we can now examine the fundamental objective of security: authorization.

Figure 10-4 provides an overview of the key authorization interfaces and classes. An important interface is ConfigAttribute, which represents a configuration setting that applies to a secure object invocation (recall a secure object invocation is any type of object that can have its invocation intercepted and security logic applied). Configuration attributes are similar to Spring's transaction attributes, such as PROPAGATION_REQUIRED.

Image from book
Figure 10-4

The AccessDecisionManager is responsible for making an authorization decision. It is passed the secure object, in case it needs access to properties of the secure object invocation (such as arguments in the case of a MethodInvocation or HttpServletRequest properties in the case of a FilterInvocation). The AccessDecisionManager is also passed the configuration attributes that apply to the secure object invocation, along with the validated Authentication object. This information is sufficient to make an authorization decision by returning void or throwing an AccessDeniedException.

Similar to the authentication-related ProviderManager, the AbstractAccessDecisionManager adopts a provider-based approach whereby it will poll a series of AccessDecisionVoters. Each voter returns a veto, grant, or deny vote. The final decision based on these votes is left to the specific AbstractAccessDecisionManager implementation.

The RoleVoter is the most commonly used AccessDecisionVoter with Acegi Security. It iterates through each configuration attribute, voting to grant access if one matches a GrantedAuthority held by the principal. As such it's a simple way of implementing role-based access control. BasicAclVoter is a more complex voter and is discussed in the following "Domain Object Instance Security" section.

In order to actually authorize a secure object invocation, Acegi Security needs to intercept the invocation to begin with. Figure 10-5 shows the key interfaces and classes involved in security interception. As shown, the AbstractSecurityInterceptor has a series of subclasses that each handle a specific type of secure object.

Image from book
Figure 10-5

The FilterSecurityInterceptor is implemented by way of a <filter> declaration in web.xml. Acegi Security provides a useful FilterToBeanProxy class, which allows a web.xml-defined Filter to be set up in the Spring bean container. This allows collaborating beans to be easily injected. MethodSecurityInterceptor is used to protect beans defined in the bean container, requiringthe use of Spring's ProxyFactoryBean or DefaultAdvisorAutoProxyCreator along with the MethodDefinitionSourceAdvisor. The AspectJSecurityInterceptor is woven in using AspectJ's compiler.

Upon detection of a secure object invocation, an AbstractSecurityInterceptor will look up the configuration attributes that apply to that invocation. If there aren't any configuration attributes, the invocation is deemed to be public and the invocation proceeds. If configuration attributes are found, the Authentication contained in the ContextHolder is authenticated via the AuthenticationManager, and the AccessDecisionManager is asked to authorize the request. If successful, the RunAsManager may replace the identity of the Authentication and then the invocation will proceed. Upon completing the invocation, the AbstractSecurityInterceptor will clean up by updating the ContextHolder to contain the actual Authentication object, and if one is defined, calling the AfterInvocationManager.

The AfterInvocationManager is able to modify the object to be returned from the secure object invocation. This is typically used to filter Collections only to contained, authorized elements, or mutate instance variables if the information is protected. It can also throw an AccessDeniedException if, for example, the principal does not have permission to the object about to be returned.

AfterInvocationManager is similar to AccessDecisionManager. It is passed the Authentication, secure object invocation, applicable configuration attributes, and of course the object to be returned. It is required to return an object, which will then become the result of the secure object invocation. A concrete implementation named AfterInvocationProviderManager is used to poll AfterInvocationProvider instances. The first instance that indicates it can process the after invocation request will be used.

Domain Object Instance Security

Protecting individual domain object instances is a common requirement of large enterprise applications. Often it is necessary to prevent unauthorized principals from accessing certain domain object instances, or preventing services layer methods from being invoked when principals are not authorized for a particular domain object instance. Other times it is desirable to mutate sensitive properties of a domain object instance, depending on the principal retrieving the instance. Acegi Security uses the secure object model discussed previously to enforce this form of ACL security within your applications.

Figure 10-6 illustrates Acegi Security's ACL services. As shown, the AclManager interface allows any Object to be presented and its ACLs obtained. An additional method is provided that allows those ACLs to be filtered to contain only ACLs that apply to the presented Authentication. The AclProviderManager polls AclProviders until one indicates it is capable of retrieving the AclEntrys applying to a given Object.

Image from book
Figure 10-6

Acegi Security provides a BasicAclProvider, which uses integer bit masking and a DAO-based approach to retrieving ACL information. The implementation basically converts the domain Object into an AclObjectIdentity, which is able to uniquely identify the Object. This is then used as a key against a BasicAclDao that can retrieve the BasicAclEntrys for the Object. BasicAclProvider (shown in Figure 10-7) uses a pluggable caching layer to avoid expensive ACL retrievals. Other interfaces are also used by the BasicAclProvider to implement the remainder of the AclProvider contract.

Image from book
Figure 10-7

Working in concert with the AclManager are several key classes. These provide the linkage between the AbstractSecurityInterceptor and actual enforcement of ACL security. These are listed in the following table.

Module

What It Does

Why and When to Use It

BasicAclEntryVoter

Called by an AbstractAccessDecisionManager, looks at method arguments of the secure object invocation and locates one that matches, and then retrieves the ACLdetails for that Object and principal, and votes to grant or deny access based on a list of allowed permissions defined against the BasicAclEntryVoter .

—Typically several BasicAclEntryVoters are used together.

—Different instances respond to different configuration attributes, with each instance typically targeting a different domain object class and/or list of allowed permissions.

—Used when you need to deny access before a secure object invocation is allowed to proceed.

BasicAclAfterInvocationProvider

Called by the AfterInvocationProviderManager, looks at the returned Object, and then retrieves the ACLdetails for that Object and principal, throwing an AccessDeniedException if the principal does not have access according to the list of allowed permissions defined against the BasicAclAfterInvocationProvider.

—If you cannot determine before a method is invoked whether the principal should be allowed access, such as getById(int) methods.

—Whenever you do not mind an AccessDeniedException being thrown after a secure object invocation is allowed to proceed.

BasicAclEntryAfterInvocation- CollectionFilteringProvider

Similar to BasicAclAfterInvocationProvider, but expects the returned Object to be a Collection and iterates that Collection, locating and removing unauthorized elements (rather than throwing AccessDeniedException) based on the list of allowed permissions defined against the BasicAclEntryAfterInvocation- CollectionFilteringProvider.

—If you need to filter a Collection to remove unauthorized elements.

—When the method returns a Collection (no other return object types are allowed).


Previous Page                                                        [ directory ]
Next Page