Forum Controls
Spotlight Features

The Rich Engineering Heritage Behind Dependency Injection

Andrew McVeigh takes us on a tour of the rich heritage behind dependency injection, what it represents, and tells us why its here to stay.

NetBeans 6: Matisse Updates

NetBeans 6 delivers great updates to the Matisse GUI builder. Spend a few minutes with Roman Strobl and get an expert briefing on what's new and what has changed.

Introduction to Groovy Part 3

In this, the third and final installation of Andres' Introduction to Groovy series, you learn about how Groovy handles variable numbers of arguments, named parameters, currying, and more about Groovy operators. Including, some new operators.

Easier Custom Components with Swing Fuse

Swing Fuse (actually just Fuse), is a framework designed to make it easier to create your own custom desktop components. In this article, Daniel Spiewak shows you how to get started and provides sample source code you can download.

Benchmark Analysis: Guice vs Spring

Willam Louth shows how he uses JXInsight Probes to investigate probable performance issues with code bases that he is not familiar with. He also highlights possible pitfalls in creating a benchmark, as well as in the analysis of results.
Replies: 6 - Pages: 1  
Threads: [ Previous | Next ]
  Click to reply to this thread Reply

Implementing Application-Specific User Details in Acegi

At 7:15 AM on Mar 14, 2007, Andrei Tudose wrote:

Introduction


It is common for enterprise applications to require specific type of information associated with each security role. For example, an administrator may have associated a person-specific data, while a customer may need to be linked with data on a company. This article deals with managing this situation using the Acegi security module, in a Spring with Hibernate web application.

The code in this article was developed using Acegi Security 1.0.3, Hibernate Annotations 3.0, Spring 2.0 and MySQL. When you read it you must be familiar with Acegi and Spring’s dependency injection. We will create an authentication details object that contains regular information (like username, password), as well as custom information, depending on the user's security role. The benefit of this is that at any place in the code of our application we can access the attributes and use them for our purposes. We will also retrieve that information using tag files in a jsp file.

The entity classes


Let’s say we need three user types for our application: "admin", "teacher" and "student". For each one we have a database table where we keep the information. The "admin" entity class could look like this:
/** Admin.java*/
package entity;
 
import java.io.Serializable;
import javax.persistence.*;
 
@Entity
public class Admin implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id;
 
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="username")
    private User userAccount;
 
    @Basic
    private String firstName;
 
    @Basic
    private String lastName;
 
    public Admin(String firstName, String lastName) {
        this.setFirstName(firstName);
        this.setLastName(lastName);
        this.setUserAccount(userAccount);
    }
 
    public Admin() {                    
    }
    //getters and setters
}

For those not familliar with Hibernate Annotations the @Basic annotation marks a field as a persistent field. By default, any field from the entity class is persistent, so "@Basic" is optional; "@OneToOne" marks a field as a one-to-one (dependent link) relation. Our "@JoinColumn(name="username")" annotation marks the link between the associated entities, through a foreign key column called "username". Also, don't forget to map the entity class by adding to the Hibernate's "hibernate.cfg.xml" file the mapping tags. For example, for "Admin.java" we will have: " ". The "Student" and "Teacher" classes are constructed in a similar way.

In our example, we will use the default structure of tables for Acegi, "Users" and "Authorities":
/** User.java*/
 
package entity;
 
import java.io.Serializable;
import javax.persistence.*;
 
@Entity
@Table(name="users")
public class User implements Serializable {
 
    @Id
    private String username;
 
    @Basic
    private String password;
 
    @Basic
    private int enabled;
 
    public User(String username, String password, int enabled) {
    this.username = username;
    this.password = password;
    this.enabled = enabled;
    }
 
    public User() {
    }
    //getters and setters
}

/** Authority.java*/
 
package entity;
 
import java.io.Serializable;
import javax.persistence.*;
 
@Entity
@Table(name="authorities")
public class Authority implements Serializable{
 
    @Id
    private String username;
 
    @Basic
    private String authority;
 
    public Authority(String username, String authority) {
        this.setUsername(username);
        this.setAuthority(authority);
    }
 
    public Authority() {
    }
    //getters and setters
}

Setting up the Acegi configuration file


To use the authentication using JDBC Dao we need to add to the dao Authentication Provider, a custom User Details Service. In the XML file (usually "applicationContext.xml") where we have our Acegi configuration we should have:
<bean id="authenticationManager" 
      class="org.acegisecurity.providers.ProviderManager">
    <property name="providers">
        <list>
            <ref bean="daoAuthenticationProvider" />
        </list>
    </property>
</bean>
 
<bean id="daoAuthenticationProvider" 
      class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
    <property name="userDetailsService">
        <ref bean="userDetailsService" />
    </property>
    <property name="userInfoObjectTypes">
        <list>
  	    <value>Teacher</value>
	    <value>Admin</value>
 	    <value>Student</value>
 	</list>
    </property>
</bean>
 
<bean id="userDetailsService" 
      class="AuthenticationJdbcDaoImpl">
    <property name="dataSource">
 	<ref bean="dataSource"/>
    </property>
</bean>
 
<bean id="dataSource" 
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName">
        <value>com.mysql.jdbc.Driver</value>
    </property>
    <property name="url">
	<value>jdbc:mysql://localhost:3306/test</value>
    </property>
    <property name="username">
	<value>user</value>
    </property>
    <property name="password">
	<value>password</value>
    </property>
</bean>

In good old fashioned Spring way, we inject in the "userInfoObjectTypes" of "AuthenticationJdbcDaoImpl", the list containing the names of the user info entity classes. To add a new type of user info, all you have to do is create a new entity class and add its name to this list.

Creating a custom User Details Service


The "AuthenticationJdbcDaoImpl" is the one that adds to a "User" object, details about a principal. By sublassing Acegi's "JdbcDaoImpl" we create our own "userDetailsService" that should look like this:
/* * AuthenticationJdbcDaoImpl.java */
 
package util.security;
 
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.acegisecurity.userdetails.jdbc.JdbcDaoImpl;
import org.springframework.dao.DataAccessException;
 
public class AuthenticationJdbcDaoImpl extends JdbcDaoImpl {   
    private String[] userInfoObjectTypes;    
    private static final SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
 
    @Override
    public UserDetails loadUserByUsername(String username) {
	try {
            UserDetails user = super.loadUserByUsername(username);
	    Session session = sessionFactory.openSession();  
	    for (int i = 0; i < userInfoObjects.length; i++) {
		Object userInfo = session.createQuery("from " + userInfoObjects[i] + " where username = '" + username + "'").uniqueResult();
		if(userInfo != null)
		    return new CustomUser(user.getUsername(), user.getPassword(), user.isEnabled(), user.getAuthorities(), userInfo);
	    }
	    return new CustomUser(user.getUsername(), user.getPassword(), user.isEnabled(), user.getAuthorities());         
	} catch (UsernameNotFoundException ex1) {
	    ex1.printStackTrace();
	    throw ex1;
	} catch (DataAccessException ex2) {
	    ex2.printStackTrace();
	    throw ex2;
	}
    }
 
    public void setUserInfoObjectTypes(String[] userInfoObjectTypes) {
	this.userInfoObjectTypes = userInfoObjectTypes;
    }
}

A "SessionFactory" object is created and populated from the "hibernate.cfg.xml" configuration file. The string vector "userInfoObjectTypes" contains the names of the entity classes corresponding to the user types. By overriding the "loadUserByUsername" from "JdbcDaoImpl", we modify the user by returning a custom user which also holds a reference to an object that contains additional information.
/** CustomUser.java */
 
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.userdetails.User;
 
public class CustomUser extends User {   
 
    private Object userInfo; 
    
    public CustomUser(String username, String password, boolean isEnabled, GrantedAuthority[] authorities, Object user) {
	super(username, password, isEnabled, true, true, true, authorities);
	this.setUserInfo(user);
    }   
    public CustomUser(String username, String password, boolean isEnabled, GrantedAuthority[] arrayAuths) {       
	super(username, password, isEnabled, true, true, true, arrayAuths);   
    }   
    public Object getUserInfo() {
        return userInfo;
    }  
    public void setUserInfo(Object userInfo) {
        this.userInfo = userInfo;
    }
}

Accessing the data from jsp


Now that we have our "CustomUser" that contains the principal authentication data and additional information about the user, the next step is to access this information from a jsp file. First of all, we will need to create a fairly simple class which contains a method that returns the current user’s data. Let’s call it "CurrentUser":
/** CurrentUser.java */
 
import org.acegisecurity.context.SecurityContextHolder;
 
public class CurrentUser {
    public CustomUser getUser() {
	return (CustomUser)(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
    }
}

Next, we create our tag file that will be used to retrieve the information from the entity object attached to the authentication object. The tag is called "message.tag" and must be placed in the directory WEB-INF/tags.
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ tag description="welcome message" pageEncoding="UTF-8" %>
 
<%@attribute name="admin" required="false" type="entity.Admin"%>
<%@attribute name="teacher" required="false" type="entity.Teacher"%>
<%@attribute name="student" required="false" type="entity.Student"%>
 
<c:choose>
    <c:when test="${not empty admin}">       
	Hello admin ${admin.firstName} ${admin.lastName} !       
    </c:when>  
    <c:when test="${not empty teacher}">      
	Hello ${teacher.rank} ${teacher.firstName} ${teacher.lastName} !
    </c:when>
    <c:when test="${not empty student}">
	Hello student ${student.firstName} ${student.lastName} email: ${student.email} year: ${student.yearOfStudy} !
    </c:when>
</c:choose>

In jsp files, the custom tag that we just defined can be used as follows:
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
 
<jsp:useBean id="currentUser" class="security.CurrentUser">
    <tags:message admin="${currentUser.user.userRole}"/>
</jsp:useBean>

Conclusion


In this article we showed how to implement in a simple manner the recurring pattern of associating specific types of information to different user roles by integrating Acegi, Spring and Hibernate. Furthermore, you can modify the "AuthenticationJdbcDaoImpl" to use your own table structure for storing authentication information.
1 . At 4:34 PM on Jun 27, 2007, Aionius Dactlys wrote:
  Click to reply to this thread Reply

Re: Implementing Application-Specific User Details in Acegi

the demo.zip that's part of your post doesn't have its hbm files. Please post those files. Thanks
2 . At 4:25 PM on Jun 29, 2007, Daniel Jue wrote:
  Click to reply to this thread Reply

Re: Implementing Application-Specific User Details in Acegi

In the article, userInfoObjectTypes is under the daoAuthenticationProvider bean, but in the code provided, it's under the userDetailsService bean.

I am assuming the supplied code is more correct.

Also I found it confusing at first glance that
CustomUser extends User , which is actually org.acegisecurity.userdetails.User , NOT the User class you wrote ( accounts.domain.entity.user ).

Overall, this is probably one of the best Acegi tutorials I've come across. The others either have a InMemoryDAO map (too simple) or don't give a good example on how to deal with custom user details. Also some are too complicated, or jump into things too quickly.

The last two code examples weren't as useful as they could have been for me, since I use Tapestry and not JSPs.
3 . At 11:36 AM on Jul 3, 2007, Andrei Tudose wrote:
  Click to reply to this thread Reply

Re: Implementing Application-Specific User Details in Acegi

Daniel, I'm sorry about that mistake, the code is correct. Aionius, I used java 5 annotations, but I'm sure you can find a plugin that can generate hbm files.
4 . At 7:04 AM on Jul 6, 2007, Olaf Geibig wrote:
  Click to reply to this thread Reply

Immutable

I'm new to acegi security and I was surfing for something about acegi and users, that's why I found your post.

I have already implemented what you are demonstrating here and you found a very nice way to do it. But I have one objection: I think you did not care about accessing the domain user objects (Student, Admin, Teacher) in a thread safe way. Your actual sources do not have setters but the comment indicates that they should be there. Remember that in the acegi api docs they undeline the importance of making the security user immutable for this reason:
http://www.acegisecurity.org/multiproject/acegi-security/apidocs/org/acegisecurity/userdetails/UserDetails.html

For instance: you get a user from the security context and get out its userInfo (domain user object). Then you are working with a reference to an instance held in the security context. If you have concurrent manipulation of such a domain user object (e.g. multiple bowser windows or tabs) it could be errorprone. What do you think?
5 . At 2:18 AM on Sep 10, 2007, ajay wrote:
  Click to reply to this thread Reply

Re: Implementing Application-Specific User Details in Acegi

HI,
i m new to Spring/Acegi ... as we were working on a similar project , i was just wondering how to deploy this demo... what dependencies are needed... actually i tried to deploy this using Ant build, is that sufficient or only Maven build is needed,( following jars used in the lib folder... but it didn't work.. help me...
acegi-security-1.0.5.jar, commons-lang-2.0.jar, commons-logging.jar, hibernate3.jar, hibernate-annotations.jar, jstl.jar, log4j-1.2.8.jar, mysql-connector-java-3.1.7-bin.jar, persistence-api-1.0b.jar, spring.jar, standard.jar, taglibs-string.jar; )
plese tell me what is missing.. any help would be highly appreciated ...
ajois4u
6 . At 7:18 PM on Nov 28, 2007, anuragkhanna wrote:
  Click to reply to this thread Reply

Re: Implementing Application-Specific User Details in Acegi

Can someone assist me in setting up encoding passwords to create users. i'm able to create users but unable to log in using the newly created user. The j_acegi_security_check returns Bad Credentials although if i post it to some different servlet i can see that the username/pwd match.

thread.rss_message