
My Web application needs both authentication and role-based authorization features. And our user profile is currently stored in an OpenLDAP server. I am looking for a security framework that can help me to integrate LDAP and provide these security features with the least amount of effort. On top of that, I want to achieve this without polluting my business logic with security code (ie. via AOP). At my first glance, Spring security (aka. acegi security) looks promising to me. After evaluating it a bit more, I believe it does provide what I need for my project. So, I started creating a prototype and gave it a trial. In this article, I will go over the steps I took to build my prototype and I will provide you the necessary explanation to move forward alongside. Hopefully, you will get over the initial learning curve as quick as possible with this guide.
Spring Security Overview
Step 1. Specify the location of the configuration files for Spring and Log4J in web.xml
The configuration below tells Spring and Log4J the location of the configuration files. These files will be parsed by the ContextLoaderListener (for Spring) and Log4JConfigListener (for log4j) during the initial loading process.
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/classes/log4j.properties</param-value> </context-param>
Step 2. Define the Acegi Filter Chain Proxy Filter in web.xml
Spring Security’s support for web security is heavily based on servlet filters. These filters intercept an incoming request and apply some security processing before the request is handled by your application. Spring security comes with a handful of filters that intercept servlet requests and pass them on to the authentication and access decision manager to enforce security. However if you ever used servlet filters, you know that for them to take effect, you must configure them in the web application’s web.xml file, using the <filter> and <filter-mapping> elements. While this works, it doesn’t lend itself to configuration using dependency injection. You have no control of the life-cycle of the filter (like instantiation), but you may be able to override the constructor and use WebApplicationContextUtil to load the bean your filter needs to act on. This is not ideal as you need to hardcode a reference to the name of the bean. That is why Filter Chain Proxy is created. The FilterToBeanProxy is a special servlet filter that, by itself, doesn’t do much. Instead, it delegate its work to a bean implements the Filter interface just like other servlet filter. In the configuration below, the target class is the filter class that I talk about. Using this approach, Spring security is able to plug in its security functionality in a modular way. NOTE: The mechanism is not Spring Security specific. You can use this approach if you have no control of the life-cycle of the class you are interested in.
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
Step 3. Define the Filter chain in ApplicationContext.xml
Now you have the proxy to redirect the request to your Spring bean. What is next? Spring Security requires at least 4 filters to be functioned. Does this mean that you have to configure a FilterToBeanProxy for each of the filters. No! To make life easier, Spring Security offers "FilterChainProxy" that can be configured to chain together several filters at once. The filters we need as part of the request processing are:
- HttpSessionContextIntegrationFilter
- Check to see if the user’s Authentication information is in Session. If so, it makes the authentication info available to the current request. At the end of the request, it will deposit the authentication info back into the session so that it will be available for the next request.
- It prevents user from logging in again.
- AuthenticationProcessingFilter
- Delegate to AuthenticationManager to do the actual authentication. AuthenticationManager determines who you are. Once you are identified, a list of roles that belongs to you will be populated. As with the rest of Spring Security, the authentication manager is a pluggable interface-based component. This makes it possible to use Spring Security with virtually any authentication mechanism.
- Process authentication based on username and password given to it in j_username and j_password.
- "filterProcessesUrl" property tells which URL it should intercept. Default to /j_acegi_security_check.
- "authenticationFailureUrl" property indicates where the user will be sent should authentication fail.
- When authentication is successful, Authentication object will be placed to the Session.
- ExceptionTranslationFilter
- Handle AuthenticationException via sending the user to the authentication entry point. It is configured in the "authenticationEntryPoint" property. There are different type of entry points: Basic, Form, Digest and X.509 cert.
- Handle AccessDeniedException - Default to HTTP 403 error to the browser. You can configure AccessDeniedHandlerImpl to forward the user to nice-looking error page.
- Without anything to handle Spring Security exceptions above, they would flow up to the servlet container and be displayed in the browser as stack trace.
- FilterSecurityInterceptor
- Enforce web security. If user has not been authenticated, throw an AuthenticationException which will be handled by exception translation filter. If user has no right to access the resource, it will throw an AccessDeniedException that will be handled by exception translation filter as well.
- It is wired with authenticationManager and accessDecisionManager
- Access Decision Manager determines whether you are authorized to access the secured resource. It performs authorization, deciding whether to let you in by considering your authentication information and the security attributes that have been associated with the secured resource. Access Decision Manager is also pluggable.
- "objectDefinitionSource" property specifies which resources (ie. urls) are secured and what privileges are required to access them via url pattern with roles.
- ChannelProcessingFilter (optional)
- Even you have done all the secure protection as stated above, the information you are authorized to obtain still needs to transfer to you via the Internet unprotected. You may want to encrypt it to prevent people from stealing it. Use HTTPS!
- ChannelProcessingFilter offers a foolproof way to ensure that certain pages be transferred using HTTPS via intercept the request, check to see if it needs to be secure and, if so, call https by redirecting the request to an HTTPS form of the original request URL.
NOTE: "securityEnforcementFilter" can combine ExceptionTranslationFilter and FilterSecurityInterceptor together.
To chain them up, here is the xml piece for FilterChainProxy.
<bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter, authenticationProcessingFilter, exceptionTranslationFilter, filterSecurityInterceptor
</value>
</property>
</bean>
You can put more than 1 pattern if you want. The order of the filters are important because it governs the order of the filters in the chain.
Step 4. Customize the authentication mechanism
Now you have all the filters wired. You may want to provide a custom authentication against your own database or ldap server. To do that, you need to implement UserDetail class and wire it up with authentication manager. Below is the method you need to override.
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException, DataAccessException {
User user = null;
GrantedAuthority[] grantedAuthorities = null;
try {
user = getUserDAO().lookupUser(userId);
if(user==null) {
throw new UsernameNotFoundException("Invalid User");
}
Set roles = user.getRoles();
int i = 0;
grantedAuthorities = new GrantedAuthority[roles.size()];
for (Iterator iter = roles.iterator(); iter.hasNext(); i++) {
Role role = (Role) iter.next();
GrantedAuthority authority = new GrantedAuthorityImpl(role.getRole());
grantedAuthorities[i] = authority;
}
} catch (DataStoreException e) {
throw new DataRetrievalFailureException("Cannot loadUserByUsername userId:"+userId+ " Exception:" + e.getMessage(), e);
}
UserDetails userDetails = new org.acegisecurity.userdetails.User(
user.getUserId(),
user.getPassword(),
user.isEnabled(), //enabled
user.isEnabled(), //accountNonExpired
user.isEnabled(), //credentialsNonExpired
user.isEnabled(), //accountNonLocked
grantedAuthorities
);
return userDetails;
}
Now you need to wire it up to your Authentication Manager
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<!-- Acegi will use our UserService bean to do authentication -->
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService"><ref bean="UserService"/></property>
<property name="passwordEncoder"><ref local="passwordEncoder"/></property>
</bean>
<bean id="UserService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="target">
<bean class="com.solutionhacker.user.UserServiceImpl" >
<property name="userDAO">
<ref local="UserDAO" />
</property>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
View-layer security
As you may notice, filters only provide a coarse-grained security, limiting access at the request level like either you can access the resource or not. In some cases, you may want more fine-grained control over what the user is allowed to see. Maybe all users of an application will be allowed to see a certain page, but only users who are granted special authority may see certain elements on that page. To handle this, Spring Security uses JSP tag library. This tag library provides only 3 tags: <authz:acl>, <authz:authentication> and <authz:authorize>. You can use this tag to wrap around the UI element and conditionally allows it to display or not based on the role. I will not go through the detail of this here. Since I advocate to use Flex as View, so I will write another article to talk about how we can achieve it in Flex.
Secure method invocation
Similar
Conclusion
As you can tell, you don’t write much java code to protect your resource. Everything is almost driven by configuration there. It is nice. However, on the other hand, the tedious work is shifted to configuration. To me, reading the configuration is harder than reading code. You can tell most of the configuration there are not coupled with application. Only the section that is application specific is the security policies you put in under "objectDefinitionSource". Again, to associate all the stuff I want to protect with the role names are tedious and hardcoded.
UPDATE: Acegi is moved to Spring Security 2.0 that has new namespace for security. The main thing they fix is to make the configuration much cleaner. I will talk about that in my next article.
UPDATE: Riable has walked us through how to upgrade from Acegi to Spring Security here. So, I don’t need to write one!
Reference
http://springtips.blogspot.com/search/label/security
http://i-proving.ca/space/Technologies/Acegi+Security+System+for+Spring
http://static.springframework.org/spring-security/site/reference/pdf/springsecurity.pdf





































(4.75 out of 5)
(4 out of 5)
No Comment Received
Sorry the comment area are closed for non registered users