Announcement Announcement Module
Collapse
No announcement yet.
Best practice for userDetails and PasswordAuthenticationDao? Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Best practice for userDetails and PasswordAuthenticationDao?

    I'm looking for some opinions on how to best implement a custom UserDetails when creating a custom PasswordAuthenticationDao especially when using Hibernate. My existing User domain model looks like this:
    Code:
    /**
     * @author kuato
     *
     * @hibernate.class table="users" 
     */
    public class User implements Serializable 
    {
    	private Long id = null;
    	private int version; /* managed version */
    	private String username;
    	private String password;
    	private String email;
    	private boolean enabled;
    	private Date created;
    	private Date lastVisit;
    	private Set roles;
    
        /* gettors and settors here */
    }
    And I'll be adding a lot more later.

    My first thought was to have User implement UserDetails but that didn't sit too well with me so I came up with a separate class and called it AuthenticatedUser which extends net.sf.acegisecurity.providers.dao.User(which of course implements UserDetails) like this:
    Code:
    public class AuthenticatedUser extends net.sf.acegisecurity.providers.dao.User
    {
        private long id;
        private String email;
        
        public AuthenticatedUser(long userId, String email, String username, String password, boolean enabled,
                GrantedAuthority[] authorities) throws IllegalArgumentException
        {
            super(username, password, enabled, authorities);
            this.id = userId;
            this.email = email;
        }
    }
    This way I can keep just the bare details about a user in the SecureContext.

    And in my custom PasswordAuthenticationDao the code looks like this to read in the roles:
    Code:
    public class UserAuthenticationDaoImpl implements PasswordAuthenticationDao 
    {
        protected final Log logger = LogFactory.getLog(getClass());
        private IUserManagerFacade userManager = null;
        
        public void setUserManager(IUserManagerFacade userManager)
        {
            this.userManager = userManager;
        }
    	
        public UserDetails loadUserByUsernameAndPassword(String username, String password)
        	throws DataAccessException, BadCredentialsException
        {      
            UserDetails userDetails = null;
             
            if (this.userManager != null) 
            {    
                User user = this.userManager.findUserByUsernameAndPassword(username, password);
                 
                if (user != null) 
                {    
                    Set roles = user.getRoles();
                    
                    Iterator it = roles.iterator();
                    GrantedAuthority[] authorities = new GrantedAuthority[roles.size()];
    				 
                     int i = 0;
                     while (it.hasNext()) 
                     {
                        Role role = (Role)it.next();
                        authorities[i++] = new GrantedAuthorityImpl(role.getName());
                     }
    				
                     logger.info("authorities= " + authorities);
    			
                     userDetails = new AuthenticatedUser(user.getId().longValue(),
                                    user.getEmail(),
                                    user.getUsername(),
                                    user.getPassword(),
                                    user.getEnabled(),
                                   authorities);
                 } else {
                     throw new BadCredentialsException("Bad Credentials Presented");
                 }
             }
            
             return userDetails;
         } 
    }
    And finally when a user navigates to a secure area where he/she can do something like add a new article/news/photo then I need to load the user object and add it to the association sort of like this in my GalleryAddPhotoFormController:
    Code:
        GalleryPhoto photo = new GalleryPhoto();
        AuthenticatedUser authUser =    (AuthenticatedUser)((SecureContext)ContextHolder.getContext()).getAuthentication().getPrincipal();
        User user = (User)this.userManager.findUserById((long)authUser.getId());
        photo.setUser(user);
    
        this.galleryManager.insertPhoto(photo);
    Is this a good practice? This keeps my User domain model loosely coupled from Acegi Security. I also don't like the idea of keeping a hibernate User object in the SecureContext the whole time. Just wondering what other people have done and what your experience has been.

    So far I just love Acegi Security. I just need to tackle the ACL permissions next.

    Thanks for listening.

  • #2
    It's really not prescriptive which way you go, provided of course you return some sort of concrete implementation of UserDetails.

    What most people do is simply add the UserDetails interface to their existing Hibernate domain object. Whilst it technically represents a coupling to the Acegi Security framework, it should be reasonably expected as your application needs to provide requisite user information to the security framework. It's only one interface, in one class, after all. The entire avoidance of coupling argument is mostly from the bad old days of superclasses instead of interfaces and EJB versus lightweight containers. There's nothing wrong, IMHO, about using interfaces where there is an obvious benefit and it is the most elegant way to do it.

    I am pleased you're liking Acegi Security, although it's the entire family of complementary Spring packages and approaches that makes application development so easy.

    Comment


    • #3
      Ben, thanks for the reply. I know it's just a matter of implementing the UserDetails interface but it also means adding a GrantedAuthorities array member if I want to use my existing User domain model. Since I'm using a top-down approach where I create the user domain model and use Xdoclet tags to generate the hbm file I at least don't have to worry about accidentially clobbering that array compared to creating a mapping file first and running hbm2java. I'm going to mull this over more.

      Thanks again.

      Comment


      • #4
        Hi kuato,
        I made it similiar to your suggestion, but what's bothering me, is that I am still wondering how to solve a rememberme service?
        Did you manage to get this working?

        Thanks a lot

        Johannes

        Comment

        Working...
        X