Transaction Management with Spring Transactions

Spring Transactions library is useful for transaction management in your Spring-Hibernate application. A transaction is a single unit of work, in other words – a group of database interactions that should happen together. If any one of these ‘database interactions’ fail then the changes of the previous interactions in the set should be undone.

Transactions are usually (depending on granularity) declared at the service layer of your application. Service layer is the logical place where your transaction should start and a typical service layer method would call 1 or more DAO layer methods.

Spring @Transantional annotation allows the developer to declare a transaction. The typical usage of @Transactional annotation would look like below:

@Service
public class ApplicationServiceImpl implements ApplicationService {

    private static final Logger logger = Logger.getLogger(ApplicationServiceImpl.class);

    @Autowired private ApplicationDao applicationDao;

    @Autowired private QueueDao queueDao;

    @Autowired private UserDao userDao;

    @Override
    @Transactional
    public void saveApplication(Application app) throws Exception {
        logger.info("saving app");
        applicationDao.saveApplication(app);
        queueDao.addToQueue(app);
        userDao.updateUser(app.getUser());
    }
}

Important to remember while using @Transactional is to define the condition for rollback. It is a common to incorrectly assume that because you’ve used @Transactional, Spring will take care of rolling back the whole transaction if something fails. This is not true. Spring only rolls back the transaction if your method throws an unchecked transaction i.e. Runtime Exception. It is much better practice to use the rollbackFor attribute instead. The rollbackFor attribute tells Spring what other situation (in addition to a runtime exception) to rollback for. In the example below I specify that transaction should be rolled back if any exception is thrown using attribute rollbackFor=Exception.class.


@Service
public class ApplicationServiceImpl implements ApplicationService {
private static final Logger logger = Logger.getLogger(ApplicationServiceImpl.class);
@Autowired private ApplicationDao applicationDao;

    @Autowired private QueueDao queueDao;

    @Autowired private UserDao userDao;

    @Override
    @Transactional(rollbackFor=Exception.class)
    public void saveApplication(Application app) throws Exception {
        logger.info("saving app");
        applicationDao.saveApplication(app);
        queueDao.addToQueue(app);
        userDao.updateUser(app.getUser());
    }
}

In addition to above, one way to ensure that DAO methods are always part of an existing transaction initiated at the service layer is to use propagation = Propagation.MANDATORY attribute in your DAO method. If all your transactions are initiated at the service layer it would be good idea of put this attribute on all your DAO methods. With this annotation in place, whenever a DAO method is called without the caller service starting a transaction, you would get an exception stating that no active transactions existed when method was invoked.

@Repository
public class ApplicationDaoImpl implements ApplicationDao {

    private static final Logger logger = Logger.getLogger(ApplicationDaoImpl.class);

    @Autowired private SessionFactory sessionFactory;

    @Override
    @Transactional(propagation = Propagation.MANDATORY)
    public void saveApplication(Application app) throws Exception {
        Session session = sessionFactory.getCurrentSession();
        session.save(app);
    }
}
Advertisement

Spring Security customising method level authorization

If you’ve used Spring Security before you’ve probably used @Secured or @PreAuthorize annotations to enable method level security. These annotations are specifically useful for preventing your web applications being attacked by malicious users. Method level security makes sure that only authorized users can execute the specific method.

Depending upon the specific problem you can either use @Secured

 @Secured({ "ROLE_DBA","ROLE_ADMIN","ROLE_SUPER_ADMIN"})
void updateUser(){

//.....

}

Or you can use @PreAuthorize

@PreAuthorize("hasRole('ADMIN', 'ROLE_DBA', 'ROLE_SUPER_ADMIN')")
void updateUser(User user){

}

However, the limitation with the above annotations is that rely on GrantedAuthorities of the in session user. In other words, you are limited to the just the roles of the user to control the access to a specific method.

What about when you want to use another attribute, lets says a STATUS determined by an external service?

Or perhaps you have there is a reasonable amount to logic involved before deciding a particular user has access to a specific controller level method of your Spring app?

Under such circumstances you can leverage the ability to call custom methods through the @PreAuthorize annotation.  You can create a custom AccessManager spring managed bean (or something else suitably named for your app)

 


@Component("accessManager")
public class AccessManager{ 

	@Autowired
	MyThirdPartyService myThirdPartyService;

	public boolean hasAccess(Authentication authentication, String... statusesNotAllowed) {
	//Complex logic can be performed here.
	//1. Access user's data with the authentication object
	//2. Access third party service to access data not available (or shouldn't be part of) in the session
	//3. statusesNotAllowed an example paramter, can have more or less
	}
}

 

The AccessManager.hasAccess() method can be called in the @PreAuthorize annotation anywhere in your application. Only when AccessManager.hasAccess method returns true with the parameters specified in the annotation, the updateUser method would get executed.


@PreAuthorize("@accessManager.hasAccess(authentication, {'CANCELLED', 'BLOCKED', 'TERMINATED'})")
public String updateUser(User user){
	....
}

 

Spring LDAP Authentication

What is LDAP? LDAP stands for Lightweight Directory Access Protocol. It’s designed to query and interact with directory data. As most enterprises use Microsoft directory service provider called Active Directory or AD, LDAP is commonly used to ‘talk’ to an Active Directory.

Developers tend to be confused about the difference between AD and LDAP. In short, AD is a database and LDAP is a protocol used to talk to an AD database.

The Java JNDI API can also be used for LDAP programming. If you’ve used JNDI API you may realize that it requires a lot more boilerplate code for even for the most simple tasks. Using Spring LDAP template on the other hand is a much more efficient way of implementing all of your LDAP related functionality.

How to use Spring-ldap? Before you write any code, you will need to import the following Spring Framework jars into your Java project. These are:

  • spring-ldap-core.jar
  • spring-core.jar and
  • spring-beans.jar

After importing the above jars, you need to create the ldapTemplate bean in spring context file. For example, I declared my ldapTemplate in the context xml as below.

  
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
 <property name="url" value="ldap://your.ad.url:389" />
 <property name="base" value="dc=your,dc=base,dc=dn" />
 <property name="userDn" value="${ad.username}" />
 <property name="password" value="${ad.password}" />
 <property name="referral" value="follow" />
 </bean>

 <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
 <constructor-arg ref="contextSource" />
 </bean>​​ 

url: the url property should be either the IP address or the URL of the Active Directory you’re using. Please make sure that you use port 689 in production. Port 689 ensures communication through LDAPS protocol which is more secure than standard LDAP. You will need to import the certificate of the Active Directory you are connecting to into your java trust store.

After you have correctly set the above properties, you will need to inject the ldapTemplate bean in your java class. In the example below I use the Spring @Autowired annotation to inject the bean into my controller class.

@Autowired LDAPTemplte ldapTemplate;

Once, you’ve injected the bean, you can finally write the java code to enable LDAP Authentication. In the snippet below I write a simple method called authenticate which authenticates a user if the correct username and password combination is supplied.


private boolean authenticate(String username, String password){
 AndFilter filter = new AndFilter();
 filter.and(new EqualsFilter("sAMAccountName", username));
 boolean authenticated = ldapTemplate.authenticate("", filter.toString(), password);
 return authenticated;
}

The above piece of code would work for you if

  • the username supplied is always be correct and
  • the user is always be enabled

This is obviously not going to be the case in real life. So we need some additional information to be returned by this method. It would nice if the method could return information related to failed password count and locked out time. This would help you understand why a user is not authenticated.

We’ll use the user search function provided by spring-ldap to return the user’s attributes. We’ll add the below code to the function we just created above.


List<Object> templates = ldapTemplate.search("", filter.toString(),new AttributesMapper() {
 @Override
 public List<Object> mapFromAttributes(Attributes attrs) throws NamingException {
 List output = new ArrayList<>();
 output.add(attrs.get("badPwdCount").get());
 output.add(attrs.get("lockoutTime").get());
 return output;
 }
});

List<Object> output = (List)templates.get(0);
logger.info("Failed Login Count: "+output.get(0).toString());
if (output.get(1) == null || output.get(1) == 0){
 logger.info("Account is not locked");
} else {
 logger.info("Account is locked")
}

The above code snippet shows how we can use the sAMAccountName attribute to search for the user. The sAMAccountName usually corresponds to the username given to the user. There is a slight chance that this might not be the case for the active directory you’re using so please just verify that sAMAccountName does actually correspond to the username used by users for logging in.

Another thing to note here is that lockoutTime returns a null value if the account has never been locked before and 0 if it’s been locked before but is not locked currently.

Obviously, the method can modified to return the values of other attributes if needed.