Friday, April 13, 2007

Customize Acegi SecurityContext

This post describes how to modify the User details stored in the Acegi Security Context.

Acegi Security uses the SecurityContextHolder object to store details of the current security context of the application. The SecurityContext holds the details of the authenticated principal in an Authentication object. By default the SecurityContextHolder uses a ThreadLocal to store these details, so that they will be available all methods in the current thread of execution.In order to obtain the Principal, you would use the following line
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Usually, the principal is an object of the type UserDetails. The UserDetails object is sort of an adapter between the security database and Acegi SecurityContextHolder. Acegi uses a UserDetailsSevice to build the UserDetails object. In order to modify the data stored Security Context of Acegi, you either have to
  • setContext() method of the SecurityContextHolder. The SecurityContext object can be set to hold an Authentication object. This will mean that you have to add some additional security code to your application code.
  • Implement the UserDetailsService and UserDetails interfaces. This way you can keep security code seperated from the application code.
This post will describe how to implement UserDetailsService and UserDetails Objects. Here's what to do to implement a UserDetailsService.
  1. Start with the implementing acegi security posts part 1 and part 2.
  2. Create the UserDetailsService:
    package authentication;

    import java.util.HashMap;
    import java.util.Map;

    import org.acegisecurity.GrantedAuthority;
    import org.acegisecurity.userdetails.UserDetails;
    import org.acegisecurity.userdetails.UserDetailsService;
    import org.acegisecurity.userdetails.UsernameNotFoundException;
    import org.springframework.dao.DataAccessException;

    public class MyUserDetailsService implements UserDetailsService {

    private Map users = init();

    private Map init() {
    Map tUsers = new HashMap();

    tUsers.put("scott", new User("scott", "tiger", "ROLE_USER"));
    tUsers.put("harry", new User("harry", "potter", "ROLE_ADMIN"));
    tUsers.put("frodo", new User("frodo", "baggins", "ROLE_USER"));

    return tUsers;
    }

    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException, DataAccessException {
    User user = (User) users.get(s);
    GrantedAuthority authority = new MyGrantedAuthority(user.getRole());
    UserDetails userDetails = new MyUserDetails(new GrantedAuthority[] { authority }, user.getUserId(), user.getPassword(), "Additional Data");
    return userDetails;
    }

    }
    MyUserDetailsService.java

    Notice that all the user data is defined in this class. The UserDetailsService has to return a UserDetails object using the User name.
  3. Create the UserDetails class:
    package authentication;

    import org.acegisecurity.GrantedAuthority;
    import org.acegisecurity.userdetails.UserDetails;

    public class MyUserDetails implements UserDetails {


    private GrantedAuthority[] authorities = null;
    private String password = null;
    private String username = null;
    private String additionalData = null;


    public MyUserDetails(GrantedAuthority[] authorities, String password, String username, String additionalData) {
    super();
    this.authorities = authorities;
    this.password = password;
    this.username = username;
    this.additionalData = additionalData;
    }

    public GrantedAuthority[] getAuthorities() {
    return authorities;
    }

    public String getPassword() {
    return password;
    }

    public String getUsername() {
    return username;
    }

    public boolean isAccountNonExpired() {
    return true;
    }

    public boolean isAccountNonLocked() {
    return true;
    }

    public boolean isCredentialsNonExpired() {
    return true;
    }

    public boolean isEnabled() {
    return true;
    }
    }
    MyUserDetails.java
  4. The User class:
    package authentication;

    public class User {

    private String userId;

    private String password;

    private String role;

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    public String getRole() {
    return role;
    }

    public void setRole(String role) {
    this.role = role;
    }

    public String getUserId() {
    return userId;
    }

    public void setUserId(String userId) {
    this.userId = userId;
    }

    public User(String userId, String password, String role) {
    super();
    this.userId = userId;
    this.password = password;
    this.role = role;
    }

    }
    User.java
  5. Modify the applicationContext.xml file: Modify the userDetailsService bean in the application context to as shown below
    <bean id="userDetailsService" class="authentication.MyUserDetailsService">
    </bean>
  6. The GrantedAuthority class: The MyGrantedAuthority class is typically used to store application roles.
    package authentication;

    import org.acegisecurity.GrantedAuthority;

    public class MyGrantedAuthority implements GrantedAuthority {

    private String authority = null;

    public MyGrantedAuthority(String authority) {
    this.authority = authority;
    }
    public String getAuthority() {
    return authority;
    }

    }
    MyGrantedAuthority.java

Tuesday, April 10, 2007

Native Queries with Hibernate Annotations

Hibernate EntityManager implements the programming interfaces and lifecycle rules as defined by the EJB3 persistence specification. Together with Hibernate Annotations, this wrapper implements a complete (and standalone) EJB3 persistence solution on top of the mature Hibernate core. In this post I will describe how map native queries (plain SQL) using Hibernate Annotations. Hibernate Annotations supports the use of Native queries through the @NamedNativeQuery and the @SqlResultSetMapping annotations.
  • @NamedNativeQuery: Specifies a native SQL named query.
  • @SqlResultSetMapping: Used to specify the mapping of the result of a native SQL query.
You will not need any EJB container support. At a minimum, you will need Hibernate core and Hibernate Annotations. The entire list of required Jar files is provided at the end.
  1. The Hibernate Configuration File: Nothing new here. Except that there are no mappings defined. I used programmatic declaration of mapping for this example as shown in the following steps.
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>
    <session-factory>
    <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
    <property name="connection.url">jdbc:oracle:thin:@localhost:1521/orcl</property>
    <property name="connection.username">scott</property>
    <property name="connection.password">tiger</property>
    <property name="dialect">org.hibernate.dialect.Oracle9Dialect</property>
    <property name="hibernate.current_session_context_class">thread</property>
    </session-factory>
    </hibernate-configuration>
    hibernate.cfg.xml
  2. The Entity class:
    package data;

    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.EntityResult;
    import javax.persistence.FieldResult;
    import javax.persistence.Id;
    import javax.persistence.NamedNativeQuery;
    import javax.persistence.SqlResultSetMapping;

    @Entity
    @SqlResultSetMapping(name = "implicit", entities = @EntityResult(entityClass = data.Employee.class))
    @NamedNativeQuery(name = "implicitSample", query = "select e.empno empNumber, e.ename empName, e.job empJob, e.sal empSalary, salg.grade empGrade from emp e, salgrade salg where e.sal between salg.losal and salg.HISAL", resultSetMapping = "implicit")
    //@SqlResultSetMapping(name = "explicit", entities = { @EntityResult(entityClass = data.Employee.class, fields = {
    // @FieldResult(name = "empNumber", column = "empno"),
    // @FieldResult(name = "empName", column = "ename"),
    // @FieldResult(name = "empJob", column = "job"),
    // @FieldResult(name = "empSalary", column = "sal"),
    // @FieldResult(name = "empGrade", column = "grade") }) })
    //@NamedNativeQuery(name = "implicitSample",
    // query = "select e.empno empno, e.ename ename, e.job job, e.sal sal, salg.grade grade from emp e, salgrade salg where e.sal between salg.losal and salg.HISAL", resultSetMapping = "explicit")
    public class Employee {

    private String empNumber;

    private String empName;

    private String empJob;

    private Double empSalary;

    private int empGrade;

    @Column
    @Id
    public int getEmpGrade() {
    return empGrade;
    }

    public void setEmpGrade(int empGrade) {
    this.empGrade = empGrade;
    }

    @Column
    public String getEmpJob() {
    return empJob;
    }

    public void setEmpJob(String empJob) {
    this.empJob = empJob;
    }

    @Column
    public String getEmpName() {
    return empName;
    }

    public void setEmpName(String empName) {
    this.empName = empName;
    }

    @Column
    public String getEmpNumber() {
    return empNumber;
    }

    public void setEmpNumber(String empNumber) {
    this.empNumber = empNumber;
    }

    @Column
    public Double getEmpSalary() {
    return empSalary;
    }

    public void setEmpSalary(Double empSalary) {
    this.empSalary = empSalary;
    }

    }
    Employee.java
    • The implitic mapping (the uncommented @NamedNativeQuery and @SqlResultSetMapping declarations are used for implicitly mapping the ResultSet to the entity class. Note that the SQL column names match the field names in the class. If the names do not match then you can set the name attribute of the @Column annotation to the column name in the query.
    • The commented @NamedNativeQuery and the @SqlResultSetMapping declarations explicitly map the fields to the columns. This will come in handy when using joins and composite keys etc.
    • Note the package definitions refer to javax.persistence and not the hibernate packages. If the packages are not declared properly, you will most likely end up with some exceptions like the following
      org.hibernate.hql.ast.QuerySyntaxException: Employee is not mapped.
      While there are other causes for this exception, the package declarations did cause a little trouble for me.
  3. The Client:
    import java.util.List;

    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.AnnotationConfiguration;
    import org.hibernate.cfg.Configuration;

    import data.Employee;

    public class Client {

    public static void main(String[] args) {
    Configuration config = new AnnotationConfiguration().addAnnotatedClass(Employee.class).configure();

    SessionFactory sessionFactory = config.buildSessionFactory();
    Session session = sessionFactory.getCurrentSession();

    List result = null;
    try {
    session.beginTransaction();

    result = session.getNamedQuery("implicitSample").list();
    System.out.println("Result size : " + result.size());
    session.getTransaction().commit();
    } catch (Exception e) {
    e.printStackTrace();
    }
    System.out.println(result.size());
    }

    }
    Client.java
  4. Jar Files: The following jar files need to be included in the classpath
    hibernate3.jar
    commons-collections-2.1.1.jar
    antlr-2.7.6.jar
    commons-logging-1.0.4.jar
    hibernate-annotations.jar
    ejb3-persistence.jar
    hibernate-commons-annotations.jar
    dom4j-1.6.1.jar
    ojdbc14.jar
    jta.jar
    log4j-1.2.11.jar
    xerces-2.6.2.jar
    xml-apis.jar
    cglib-2.1.3.jar
    asm.jar
    All the Jar files will be available in the hibernate download. The hibernate-annotations.jar file is available in the hibernate annotations download.
This example was tested on Java 5 Update 9 with Hibernate version 3.2.3 and Hibernate Annotations Version: 3.3.0.GA.

Tuesday, April 03, 2007

Using JSON from Java

JSON (JavaScript Object Notation) is a lightweight data-interchange format based on a subset of the JavaScript Programming Language. JSON object structure is built on two structures:
  • A collection of name/value pairs: An associative array in Javascript which is analogous to the Java Map.
  • An ordered list of values: Analogous to a Java Array.
The rest of the datatypes (string, number, true/false etc.) have straight forward mappings to the Java datatypes.

Producing JSON from Java


Since JSON objects can be declared literally in JavaScript, it is possible to create these literals from Java classes. There are a couple of ways to achieve this
  • Using JSONObject: The source code for JSONObject and the supporting classes is available at http://www.json.org/java/index.html. Using this you can create JSONObject from a Java class as shown below
      JSONObject json = new JSONObject();
    json.put("name1",value1);
    json.put("name2",value2);
    The JSONObject class will appear as a Javascript Associate array which looks like this
     {"name1": "value1",
    "name2": "value2"}
  • Using JSON-lib: This is a Java library (available on sourceforge) that provides utility methods to convert different Java objects into JSONObject. This method is used in the example shown below.
The following Java class and JSP demonstrate how to use JSON with Java
  1. The Java class:
    package beans;

    import java.util.Map;
    import java.util.TreeMap;

    import net.sf.json.JSONObject;

    public class MapHolder {
    private Map map;

    public Map getMap() {
    return map;
    }

    public void setMap(Map map) {
    this.map = map;
    }

    public JSONObject getJsonObject() {
    JSONObject obj = JSONObject.fromMap(map);
    return obj;
    }

    public MapHolder() {
    map = new TreeMap();
    map.put("name1", "value1");
    map.put("name2", "value2");

    }
    }
    MapHolder.java
    • The getJsonObject() method uses JSON-lib to convert the Map to a JSONObject.
  2. The JSP:
    <%@page import="beans.MapHolder,net.sf.json.JSONObject"%>
    <html>
    <head>
    <title>Title</title>
    <script>
    function getInfo(o)
    {
    var obj=o;
    document.getElementById('test').innerHTML=obj.name1;
    }
    </script>
    </head>
    <body>
    <%
    JSONObject obj = (new MapHolder()).getJsonObject();
    %>
    <input type="button" value="submit" onclick='getInfo(<%=obj%>)' />

    <div id="test"></div>
    </body>
    </html>
    index.jsp
    • Note the "obj.name1" in the script to access the element from the associative array.

  3. The JAR files: In order to use JSON-lib, you will need to include the following jars in your classpath

Popular Posts