Developers have a wide range of choices in how to implement security within enterprise applications. Let's start by reviewing the typical requirements of such applications and three major security platforms commonly used with Spring.
Although Spring provides a highly flexible framework, most of its users leverage its capabilities to deliver robust, multi-tier enterprise applications. Such applications are usually data-centric, have multiple concurrent users, and are of significant importance to the core business. Hence most enterprise applications must provide a robust approach to security.
Let's briefly recap some of the key terms used when discussing computer security. The term principal refers to a user, service, or agent who can perform an operation. A principal presents credentials such as a password in order to allow authentication, which is the process of establishing the identity of a caller. Authorization refers to the process of determining whether a principal (authenticated or non- authenticated) is permitted to perform a given operation.
Four main security concerns must be addressed in typical enterprise applications:
Authentication: Enterprise applications typically need to access a variety of authentication repositories. Often these repositories will be in the form of LDAP directories, CRM systems, enterprise single-sign-on solutions, and autonomous, application-specific databases. Depending on the repository, the server may never expose the credentials (in which case authentication is performed only by binding to the repository with the correct credentials) or the credentials may be in a hashed format. Each authentication repository also must track the authorities granted to a principal. Security frameworks must integrate with these types of repositories, or new ones, sometimes simultaneously. There may also be a range of client types for an enterprise application, including web applications, Swing clients, and web services. The security framework needs to deal consistently with authentication requests from any such client type.
Web request security: Many enterprise applications are web-based, often using an MVC framework and possibly publishing web services. Security is often required to protect URI patterns. In addition, web views often require integration with a security framework so content can be generated based on the authorities held by the principal.
Service layer security: Service (business) layers should be secured in all but the simplest of applications. Security is usually best modeled as an aspect. Using an AOP-based solution allows service layer implementations to be largely or completely unaware of security. It also eliminates the error-prone and tedious approach of enforcing security in user interface tiers via techniques such as URI pattern filtering. Acegi Security can secure an AOP Alliance MethodInvocation, using Spring AOP. It can also secure an AspectJ JoinPoint.
Domain object instance security: Java applications generally use domain objects to model the problem domain. Different instances of these domain objects may require different security. A principal may have delete permission to one domain object instance but only read permission to a different domain object instance. Permissions are assigned to a recipient, which refers to a principal or a role. The list of permissions assigned to different recipients for a given domain object instance is known as an access control list, or ACL.
The following common computer security concern is not typical of enterprise applications:
Limited privilege execution: Some Java applications need to execute with limited privileges, as the user is concerned about the trustability of the application. The most common examples are applets, which run within a web browser, or Java Web Start applications, which can automatically download from the Internet. Enterprise applications are typically trusted by the computer executing them, such as a corporate server. As such, it is of unnecessary complexity for most enterprise applications to execute with limited privileges.
Given these requirements, we recommend the Acegi Security System for Spring (http://acegisecurity.sourceforge.net; Apache 2 license) to handle the security requirements of Spring-managed applications. We will now briefly explore Acegi Security with reference to the other major security platforms that target enterprise application development.
Acegi Security is widely used within the Spring community for providing comprehensive security services to Spring-powered applications. It comprises a set of interfaces and classes that are configured througha Spring IoC container. In the spirit of Spring, the design of Acegi Security allows many applications to implement the four common enterprise application security requirements listed previously solely via declarative configuration settings in the IoC container. Acegi Security is heavily interface-driven, providing significant room for customization and extension.
Acegi Security, like Spring, emphasizes pluggability. It is based on the assumption that there is no one-size-fits-all approach for security, instead providing a consistent programming model from a security perspective, while allowing a choice between different strategies to implement key security responsibilities.
We will now briefly examine the four common enterprise application security requirements (authentication, web request security, service layer security, and domain object instance security), and how Acegi Security addresses each. Later in this chapter we will revisit Acegi Security architecture and configuration in more detail, so don't be concerned if you don't follow everything mentioned in the following quick overview.
Before the other three security requirements can be addressed, the principal must be authenticated. Acegi Security performs authentication using a pluggable AuthenticationManager, which can be defined in a Spring application context as follows:
<bean > <property name="providers"> <list> <ref local="daoAuthenticationProvider"/> </list> </property> </bean> <bean > <property name="authenticationDao"><ref local="jdbcDaoImpl"/></property> </bean> <bean > <property name="dataSource"><ref bean="dataSource"/></property> </bean>
The authenticationManager bean will delegate through the list of authentication providers (the preceding code sample shows only one). Authentication providers are capable of authenticating from a given authentication repository. In this case, the daoAuthenticationProvider bean will be used for authentication, which uses a data access object to retrieve the user information.
Acegi Security provides web request security via FilterSecurityInterceptor and Security EnforcementFilter. The latter class is implemented as a web.xml filter, with the filter itself defined in the application context:
<bean > <property name="filterSecurityInterceptor"> <ref local="filterInvocationInterceptor"/> </property> <property name="authenticationEntryPoint"> <ref local="authenticationProcessingFilterEntryPoint"/> </property> </bean> <bean > <property name="loginFormUrl"><value>/acegilogin.jsp</value></property> </bean> <bean > <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 /secure/super/**=ROLE_WE_DONT_HAVE /secure/**=ROLE_SUPERVISOR,ROLE_USER </value> </property> </bean>
Most of the preceding is boilerplate code, with the actual URI patterns to protect being defined in the filter InvocationInterceptor bean objectDefinitionSource property. As you can see, the filter InvocationInterceptor bean uses Apache Ant–style paths to express the URI patterns. In addition to Apache Ant paths, regular expressions are supported. The CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON directs the filter to match URI patterns in a case-insensitive manner. These declarations would prevent a principal from accessing the /secure URI unless they held one of the two indicated roles, and would prevent /secure/super being accessed unless the principal held the ROLE_WE_DONT_HAVE role.
Comprehensive services layer security is provided by Acegi Security, again using the application context. Here we define an AOP Alliance method interceptor, which is autowired via Spring's DefaultAdvisor AutoProxyCreator:
<bean /> <bean autowire="constructor"/> <bean > <property name="authenticationManager"> <ref bean="authenticationManager"/> </property> <property name="accessDecisionManager"> <ref local="businessAccessDecisionManager"/> </property> <property name="objectDefinitionSource"> <value> com.acegitech.BankManager.createAccount=ROLE_TELLER com.acegitech.BankManager.deposit=ROLE_TELLER com.acegitech.BankManager.approveLoan=ROLE_MORTGAGE_CENTRE com.acegitech.BankManager.viewAuditTrail=ROLE_INVESTIGATIONS </value> </property> </bean>
In this example, the MethodSecurityInterceptor will intercept any BankManager implementation defined in the application context. The syntax of MethodSecurityInterceptor is very similar to Spring's transaction support, allowing both fully qualified method names as well as wildcards (for example, create*=ROLE_TELLER) to be used. If the indicated roles are not held, MethodSecurityInterceptor will throw an Access DeniedException. If the principal is not logged in, an AuthenticationException will be thrown.
Finally, domain object instance security in Acegi Security leverages the services layer security interceptors. Acegi Security also provides a repository, known as an AclManager, that can determine the access control list (ACL) that applies for any domain object instance it is passed:
<bean > <property name="processConfigAttribute"> <value>ACL_FOLDER_WRITE</value> </property> <property name="processDomainObjectClass"> <value>com.acegitech.Folder</value> </property> <property name="aclManager"><ref local="aclManager"/></property> <property name="requirePermission"> <list> <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/> <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.WRITE"/> </list> </property> </bean> <bean > <property name="allowIfAllAbstainDecisions"><value>false</value></property> <property name="decisionVoters"> <list> <ref local="roleVoter"/> <ref local="aclFolderWriteVoter"/> </list> </property> </bean> <bean > <property name="authenticationManager"> <ref bean="authenticationManager"/> </property> <property name="accessDecisionManager"> <ref local="businessAccessDecisionManager"/> </property> <property name="objectDefinitionSource"> <value> com.acegitech.FolderManager.createFolderWithParent=ACL_FOLDER_WRITE com.acegitech.FolderManager.writeFolder=ACL_FOLDER_WRITE com.acegitech.FolderManager.readFolder=ROLE_USER </value> </property> </bean>
The preceding settings would cause Acegi Security to intercept every method invocation for the listed methods of FolderManager. For the two methods marked ACL_FOLDER_WRITE, the aclFolder WriteVoter would be asked to vote on access. The aclFolderWriterVoter would then query the AclManager (which is not shown in the preceding sample) for the ACL entries applying to the current principal. If that returned list contains a "read" or "administration" permission, the method invocation would be allowed to proceed.
Acegi Security also publishes AuthenticationEvent and SecurityInterceptionEvent objects for authentication and authorization operations respectively to the Spring ApplicationContext. This provides a convenient hook for loosely coupled auditing, logging, and monitoring systems.
Let's now consider the alternative options available for securing Spring-based enterprise applications.
The Java Authentication and Authorization Service (JAAS) was introduced as an optional package to the Java 2 SDK version 1.3, being integrated into the SDK from version 1.4. JAAS provides pluggable authentication and authorization services for the Java platform, integrating into the platform layer security via system security policies. Many web and EJB containers provide some form of JAAS support, although, as of early 2005, there are significant compatibility issues between the various containers.
JAAS uses a Subject to represent an actual person or other entity. When authenticated, a range of identities is added to the Subject in the form of Principal objects. A JAAS Principal is used to represent an identity of the Subject, such as its name. Subjects can also contain public and private credentials, such as X.509 certificates.
Authentication is handled by JAAS through a LoginModule, a list of which is typically contained in an external policy file. A LoginModule is responsible for authenticating the principal and populating the Subject. A LoginModule requires a CallbackHandler to collect credentials from the principal.
Acegi Security provides an authentication provider that can delegate to JAAS, allowing integration between the two systems for authentication.
Once authenticated, JAAS's integration with the platform security, via a SecurityManager, allows authorization to take place. Authorization is typically defined in an external policy file. The operation of JAAS authorization is involved and beyond the scope of this book, although interested readers can refer to the Java 2 SDK documentation for a detailed background.
The following table briefly discusses some of the reasons commonly provided for using JAAS.
Why It Matters
Mitigating Factors and Alternatives
Range of LoginModules
Saves you from having to write a login module for a given authentication repository.
—Most authentication repositories are relatively easy to integrate with in the first place, which means that it is easy to write a new authentication provider for a framework such as Acegi Security.
—Acegi Security can use JAAS for authentication, effectively making all its LoginModules available to Acegi Security–managed applications.
In a limited privilege execution environment, the JVM can ensure the code itself as well as that the principal is not requesting an unauthorized operation.
—Acegi Security, on the other hand, requires each secure object to be intercepted, typically via a Filter or an AOP“around” advice.
—As Acegi Security provides AspectJ support, AOP“around” advices can even be applied to POJOs without any special step such as creating a proxy through an application context.
—Rarely is the code of enterprise applications required to execute in a limited privilege environment.
—Significant configuration is required for JAAS authorization, often involving JAR deployments in the SDK lib directory and editing of external policy files.
—Acegi Security’s use of the applica- tion context means Spring users find it a natural and convenient approach to configuration, particularly given its similarity (both in terms of security configuration and AOPinterception) with Spring transaction management.
—Many web and application con- tainers are incompatible with JVM-level authorization and cannot, for example, support two web applications using different security policies.
—Using JVM-level authorization will limit web and application server portability, whereas using Acegi Security in its recommended configuration delivers complete compatibility and portability.
—Even if JVM-level authorization was necessary for an unusual enterprise application, it would only be as secure as the security policy, and as that security policy will likely be provided by the same people who wrote the enterprise application itself, it becomes debatable just how much more security is realistically being derived from JVM-level authorization. Consider using AspectJ with Acegi Security instead, to gain portability and configuration benefits.
Automatic propagation of security identity from a web container to an EJB container, or from one EJB container to a remote EJB container
If you’re using EJBs, this means less work for developers.
—Most new Spring applications don’t use EJBs.
—As most EJB developers are acutely aware, the EJB specification is limited in terms of authorization expressiveness and this typically requires an application to perform authorization itself anyway.
—Through container adapters, Acegi Security can provide authentication for EJB containers, and in doing so provide a Principal simultaneously acceptable to both the EJB security subsystem as well as the Acegi Security framework.
—Acegi Security performs transpar- ent remote propagation of security identity when using Spring’s RMI or HttpInvoker, effectively delivering an equivalent security propagation benefit as JAAS for remote POJOs.
JAAS is an official standard
In some cases this is important for funding or buy-in.
—Acegi Security is the de facto and official standard for securing Spring-based applications, and as such has a large and active community with peer design review, implementation patterns, comprehensive documentation, extensions, and support readily available.
—JAAS is only occasionally used in enterprise applications for authen- tication (usually the web or EJB container is relied upon for authentication) and it is rarely used for authorization because of the compatibility constraints.
—Acegi Security can use JAAS for authentication if desired, effectively equalling the level of JAAS support available in most practical cases to enterprise application developers who consider the standard issue an important one.
Certainly JAAS is an important standard when developing applications intended for a limited privilege execution environment, such as an applet. However, unless web and EJB container support for JAAS greatly improves, it is unlikely to prove practical for efficiently developing portable, secure enterprise applications.
The Servlet Specification provides a simple way of authenticating principals and securing web URIs.
Each web container is responsible for performing authentication. Typically each web container will provide its own interface that authentication providers must implement to perform authentication. After configuring the web container to use a particular implementation of that interface, web.xml is used to finalize the authentication configuration:
<login-config> <auth-method>FORM</auth-method> <realm-name>Servlet Specification Secured Realm</realm-name> <form-login-config> <form-login-page>/login.jsp</form-login-page> <form-error-page>/login.jsp?login_error=1</form-error-page> </form-login-config> </login-config>
Because each web container has its own special authentication provider interface, this introduces web container portability constraints. In addition, the web container will often require those authentication providers to be placed in a common library directory (along with support JARs such as JDBC drivers) and this further complicates configuration and classloader operation.
As far as authorization goes, the Servlet Specification provides just one of the three types of authorization areas typically required by enterprise applications: web request security. Again, web.xml is used to specify the roles that are required for given URI patterns:
<security-constraint> <display-name>Secured Area Security Constraint</display-name> <web-resource-collection> <web-resource-name>Secured Area</web-resource-name> <url-pattern>/secure/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>ROLE_USER</role-name> <role-name>ROLE_SUPERVISOR</role-name> </auth-constraint> </security-constraint>
The major limitation with this approach to web request authorization is that authorization decisions cannot take into account anything but roles. For example, they cannot consider in the authorization logic if a request has a particular query parameter. In addition, the specification provides only one way to express the URI patterns to be secured. (Acegi Security interprets two standard formats, with additional formats easily added.)
As mentioned already, the absence of support for services layer security or domain object instance security translates into serious limitations for multi-tiered applications. Typically developers find themselves either ignoring these authorization requirements, or implementing the security logic within their MVC controller code (or even worse, inside their views). There are disadvantages with this approach:
Separation of concerns: Authorization is a crosscutting concern and should be implemented as such. MVC controllers or views implementing authorization code make it more difficult to test both the controller and authorization logic, more difficult to debug, and will often lead to code duplication.
Support for rich clients and web services: If an additional client type must ultimately be supported, any authorization code embedded within the web layer is non-reusable. It should be considered that Spring remoting exporters export only service layer beans (not MVC controllers). As such, authorization logic needs to be located in the services layer to support a multitude of client types.
Layering issues: An MVC controller or view is simply the incorrect architectural layer to implement authorization decisions concerning services layer methods or domain object instances. Though the Principal may be passed to the services layer to enable it to make the authorization decision, doing so would introduce an additional argument on every services layer method. A more elegant approach is to use a ThreadLocal to hold the Principal, although this would likely increase development time to a point where it would become more economical (on a cost-benefit basis) to simply use a dedicated security framework.
Authorization code quality: It is often said of web frameworks that they "make it easier to do the right things, and harder to do the wrong things." Security frameworks are the same, because they are designed in an abstract manner for a wide range of purposes. Writing your own authorization code from scratch does not provide the "design check" a framework would offer, and in-house authorization code will typically lack the improvements that emerge from widespread deployment, peer review, and new versions.
For simple applications, Servlet Specification security may be sufficient. Although when considered within the context of web container portability, configuration requirements, limited web request security flexibility, and non-existent services layer and domain object instance security, it becomes clear why developers often look to alternative solutions.