But to implement the Authentication and Authorization in the web application I wanted to use Spring Security Framework 3. But it had a different mechanism to connect with the database and retrieve the user credentials and authorizations.
I wanted the same EJB Session Beans which handles User related logics to be used in the authentication as well. Then after reading some materials online and some coding I managed to use my EJB Session Bean UserController along with the Spring Security authentication and authorization mechanism.
package com.lkbotics.business;
import com.lkbotics.entity.Country;
import com.lkbotics.entity.Customer;
import com.lkbotics.entity.User;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
/**
*
* @author Shazin Sadakath
*/
@Stateless
public class UserController implements UserControllerRemote, UserControllerLocal {
/**
* Entity Manager for Accessing Tables in Database
*/
@PersistenceContext(name="LKBotics")
private EntityManager entityManager;
/**
*
* @param username - Username of the user
* @return User
*/
public User getUserByUsername(String username) {
Query query = entityManager.createQuery("FROM User as u WHERE u.username = ?1");
query.setParameter(1, username);
User user = (User) query.getSingleResult();
return user;
}
Spring Security 3 Uses something called an Authentication Provider to get Authentication details. This could be an XML Tag with hard coded values or a database table with dynamic values. But in this scenario we require a custom Authentication Provider which uses the EJB Session Bean UserController to get the user credentials. For that purpose Spring Security 3 provides an Interface named UserDetailsService.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.lkbotics.services;
import com.lkbotics.business.UserControllerRemote;
import com.lkbotics.dto.UserDetailsAdaptor;
import com.lkbotics.entity.User;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.NoResultException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
*
* @author Shazin Sadakath
*/
@RequestScoped
public class UserDetailsServiceImpl implements UserDetailsService{
private UserControllerRemote userController;
public UserDetailsServiceImpl(){
try {
InitialContext ctx = new InitialContext();
userController = (UserControllerRemote) ctx.lookup("com.lkbotics.business.UserControllerRemote");
} catch (NamingException ex) {
Logger.getLogger(UserDetailsServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetailsAdaptor userDetails = null;
try{
User user = userController.getUserByUsername(username);
userDetails = new UserDetailsAdaptor(user);
}catch(NoResultException e){
throw new UsernameNotFoundException(username);
}
return userDetails;
}
}
As seen above UserDetailsService has a method named loadUserByUsername which needs to be overridden and implemented to use the EJB3 Session Bean. This enable user credentials to be retrieved via the EJB instead of direct JDBC calls, centralizing the data access to EJB Persistence Context.
Hope this helped.
Great post, not many have blogged about this yet, thanks for sharing :)
ReplyDeletethanks for the tutorial but I'm asking if we can do this somehow differently, that's to say without calling the ejb with the lookup method?
ReplyDelete