Skip navigation

Tag Archives: Servlet

Access Control List (ACL) is a way to control access for user in an application. This page contains Access Control List sample using Java, this article show an ACL demo application in Java web application, that i hope will make better understanding of what Access Control List (ACL) is. You can find resources that will explain what ACL is, but i think demo application is more effective for understanding about ACL.

Download ACL demo application from ziddu and 4shared, it’s a Java web application, furthermore it’s Songs database application. The application is using following technology/library : JSP, Servlet, EL, Hibernate/JPA. For simplicity, i’m using JSP and servlet for presentation layer and controller, and Hibernate for database layer so i dont have to deal with SQL (i hate it :p ). This application is an IntelliJ IDEA project, you need to re-create project if you are using Eclipse or Netbeans, and add the classes/library accordingly. I use Apache Tomcat 6 for application server.

Note: Because Tomcat is actually a Servlet container rather Application Server (e.g Glassfish, Geronimo, JBoss, etc), basically Tomcat dont have Java EE library, while i’m using Hibernate/JPA annotations (for object-table database mapping) which is using Java EE library, you need to place javaee.jar in “<TOMCAT_HOME>/lib” folder before running tomcat, DO NOT put javaee.jar in application lib directory, it’s against Java Servlet specification

You may want to edit “hibernate.cfg.xml” first to match your environtment. I’m using Mysql database for this demo. Before running the application please run “acl.demo.util.SetUpACLDemoData” class, this class will set up the data on database so the application can run. Now run the application server, open the browser and go to http://localhost:8080/ACLDemo, this will open login page.
Login page

We can’t see other page if we hasn’t login, I create a SecurityFilter class for this, it’s a Servlet Filter that will intercept all request, checked whether there’s user session in session, if it’s not it will send us to login page.

To login, we can use one of the following account:
– admin/admin (Administrator)
– zidane/password (SuperAdministrator)
– kabayan/kabayan (DataEntry)
– tamara/tamara (Administrator)

The one in the parentheses is the type of a user. Type of a user will determine his/her rights of accessing something (e.g resources, page, module, etc), different user type will have different rights for accessing some pages (e.g “DataEntry” user can’t access “Administrator” level page). You can try to login with above different account and see not all the user have the same rights access. We will examine how this is happen, this is what Access Control List is.

Now login as “SuperAdministrator” so we can see all the page/module. As mention earlier this demo is a Songs database application, it’s had modules such as “Song”, “Album”, “Singer”, and “Genre”, which actually is a dummy page. It’s static, I don’t get the data from database and there’s no corresponding table also. Other modules is “ACL”, “Users”, and “UserType”, which corresponding to what we are talking about, the Access Control List (ACL). We retrieve the data from databases, as well we can add or edit, but for simplicity i don’t add delete/remove functionality. Module “ACL”, “Users”, and “UserType” are important component in this version of ACL’s, they collaborate each other determine how a user have the rights to access a modules or to do some action (e.g Insert, Update, Delete).

index.jsp

This is the dummy modules:

This is the corresponding ACL’s modules:
UserType

User

Access Control List

In this demo to manage an ACL we start by creating a user type then assign that user type to some user. Everytime we make new user type, the application also make ACL’s for that user type. Let’s make a “UserType” that i will called it “Editor” and see what happen.

Add UserType

ACL

After we save the “Editor” user type, it will redirect us to ACL page. We can see there’s ACL’s for “Editor” user type created by application for us, what we only need to set is what modules and what action that the “Editor” can access. Let’s say I just want the “Editor” has the rights to update/edit every module except administrator level module, I’ll set the ACL’s for “Editor” like below picture (note: module “ACL” and “User” is administrator level module)

ACL

Then make a User and assign the “Editor” user type to him/her (I do not add functionality to edit user type of a User). My new user will look like this:
Add User

List User

Please logout and login as “Editor” user. We can see that the menu in front page is not as complete as Administrator user, also the user can’t add new or delete record.

Index.jsp Songs Albums Singers Genres

Now let we examine the code. The code structure look like this:
Code structure

Now let’s see why  some buttons or links can “missing” or display according to type of  user account. In almost every buttons or links that I want to restrict, I put code something like this;

....
<c:if test="<%=aclManager.allowUpdate(session, Permission.ACL)%>">
      <a class="toplink" href="deleteSong.jsp?code=S0001">delete</a>
</c:if>
....
....
<c:if test="<%=aclManager.allowDelete(session, Permission.SONG)%>">
	<input class="kotaktombol" name="save" type="submit" value="Save" />
</c:if>
....

aclManager” is a “acl.demo.manager.AccessControlListManager” class, because I know that there’s “acl.demo.entity.UserSession” object on the Session, i just send “session” object as parameter to method like “allowView”, “allowInsert”, etc, to determine the user type. The “acl.demo.enums.Permission” is to determine the existing modules and what module the page is.
AccessControlListManager

package acl.demo.manager;

import acl.demo.entity.AccessControlList;
import acl.demo.entity.UserType;
import acl.demo.entity.UserSession;
import acl.demo.enums.Permission;
import acl.demo.util.HibernateUtil;

import javax.servlet.http.HttpSession;
import java.util.List;

public class AccessControlListManager extends BaseManager {
    public AccessControlList newAccessControlList() throws Exception {
        return new AccessControlList();
    }

    public AccessControlList get(Long id) throws Exception {
        return (AccessControlList) getEntity(AccessControlList.class, id);
    }

    public AccessControlList save(AccessControlList entity) throws Exception {
        saveEntity(entity);

        return entity;
    }

    public AccessControlList update(AccessControlList entity) throws Exception {
        updateEntity(entity);

        return entity;
    }

    public void delete(AccessControlList entity) throws Exception {
        deleteEntity(entity);
    }

    public List listAll() throws Exception {
        return listAll(AccessControlList.class);
    }

    public AccessControlList getByUserTypeAndPermission(UserType userType, Permission permission) throws Exception {
        StringBuffer query = new StringBuffer("from AccessControlList acl ");
        query.append("where acl.userType = :userType and acl.permission = :permission");

        AccessControlList acl = (AccessControlList) HibernateUtil.getSession().createQuery(query.toString())
                .setParameter("userType", userType).setParameter("permission", permission)
                .uniqueResult();

        return acl;
    }

    ..........

    public boolean allowView(HttpSession session, Permission permission) throws Exception {
        return this.getByUserTypeAndPermission(((UserSession) session.getAttribute("userSession")).getUser().getUserType(), permission).isCanView();
    }

    public boolean allowInsert(HttpSession session, Permission permission) throws Exception {
        return this.getByUserTypeAndPermission(((UserSession) session.getAttribute("userSession")).getUser().getUserType(), permission).isCanInsert();
    }

    public boolean allowUpdate(HttpSession session, Permission permission) throws Exception {
        return this.getByUserTypeAndPermission(((UserSession) session.getAttribute("userSession")).getUser().getUserType(), permission).isCanUpdate();
    }

    public boolean allowDelete(HttpSession session, Permission permission) throws Exception {
        return this.getByUserTypeAndPermission(((UserSession) session.getAttribute("userSession")).getUser().getUserType(), permission).isCanDelete();
    }
}

Permission

package acl.demo.enums;

public enum Permission { SONG, SINGER, ALBUM, GENRE, USER, ACL }

UserSession

package acl.demo.entity;

import java.util.Date;

public class UserSession {
    private User user;
    private Date loginTime;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Date getLoginTime() {
        return loginTime;
    }

    public void setLoginTime(Date loginTime) {
        this.loginTime = loginTime;
    }
}

For object-table mapping I use Hibernate/JPA annotations.
User

package acl.demo.entity;

import javax.persistence.*;

@Entity
@Table(name = "USERS")
public class User {
    private Long id;
    private String username;
    private UserType userType;
    private String password;

    @Id
    @GeneratedValue
    @Column(name = "ID")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Basic
    @Column(name = "USERNAME")
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @ManyToOne(targetEntity = UserType.class)
    @JoinColumn(name = "USER_TYPE_ID")
    public UserType getUserType() {
        return userType;
    }

    public void setUserType(UserType userType) {
        this.userType = userType;
    }

    @Basic
    @Column(name = "PASSWORD")
    public String getPassword() {
        return password;
    }

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

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[ID=" + this.getId());
        sb.append(", USERNAME=" + this.getUsername());
        sb.append(", USERTYPE=" + this.getUserType().getType());
        sb.append(", PASSWORD=" + this.getPassword());
        sb.append("]");

        return sb.toString();
    }
}

UserType

package acl.demo.entity;

import javax.persistence.*;
import java.util.List;
import java.util.ArrayList;

@Entity
@Table(name = "USER_TYPE")
public class UserType {
    private Long id;
    private String type;
    private List<User> users = new ArrayList<User>();

    @Id
    @GeneratedValue
    @Column(name = "ID")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Basic
    @Column(name = "TYPE")
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @OneToMany(targetEntity = User.class, mappedBy = "userType")
    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[ID=" + this.getId());
        sb.append(", TYPE=" + this.getType());
        sb.append(", USERS=" + this.getUsers());
        sb.append("]");

        return sb.toString();
    }

AccessControlList

package acl.demo.entity;

import acl.demo.enums.Permission;

import javax.persistence.*;

@Entity
@Table(name = "ACCESS_CONTROL_LIST")
public class AccessControlList {
    private Long id;
    private Permission permission;
    private UserType userType;
    private boolean canView;
    private boolean canInsert;
    private boolean canUpdate;
    private boolean canDelete;

    @Id
    @GeneratedValue
    @Column(name = "ID")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Enumerated(value = EnumType.STRING)
    @Column(name = "PERMISSION")
    public Permission getPermission() {
        return permission;
    }

    public void setPermission(Permission permission) {
        this.permission = permission;
    }

    @ManyToOne(targetEntity = UserType.class)
    @JoinColumn(name = "USER_TYPE_ID")
    public UserType getUserType() {
        return userType;
    }

    public void setUserType(UserType userType) {
        this.userType = userType;
    }

    @Basic
    @Column(name = "CAN_VIEW")
    public boolean isCanView() {
        return canView;
    }

    public void setCanView(boolean canView) {
        this.canView = canView;
    }

    @Basic
    @Column(name = "CAN_INSERT")
    public boolean isCanInsert() {
        return canInsert;
    }

    public void setCanInsert(boolean canInsert) {
        this.canInsert = canInsert;
    }

    @Basic
    @Column(name = "CAN_UPDATE")
    public boolean isCanUpdate() {
        return canUpdate;
    }

    public void setCanUpdate(boolean canUpdate) {
        this.canUpdate = canUpdate;
    }

    @Basic
    @Column(name = "CAN_DELETE")
    public boolean isCanDelete() {
        return canDelete;
    }

    public void setCanDelete(boolean canDelete) {
        this.canDelete = canDelete;
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[ID=" + this.getId());
        sb.append(", PERMISSION=" + this.getPermission());
        sb.append(", USERTYPE=" + this.getUserType().getType());
        sb.append(", CAN_VIEW=" + this.isCanView());
        sb.append(", CAN_INSERT=" + this.isCanInsert());
        sb.append(", CAN_UPDATE=" + this.isCanUpdate());
        sb.append(", CAN_DELETE=" + this.isCanDelete());
        sb.append("]");

        return sb.toString();
    }
}

hibernate.cfg.xml

<hibernate-configuration>
    <session-factory>
        <property name="connection.url">jdbc:mysql://localhost:3306/test</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <!-- DB schema will be updated if needed -->
        <property name="hbm2ddl.auto">update</property>

        <!-- MAPPING -->
        <mapping class="acl.demo.entity.UserType" />
        <mapping class="acl.demo.entity.User" />
        <mapping class="acl.demo.entity.AccessControlList" />
    </session-factory>
</hibernate-configuration>

SecurityFilter looks like this:

package acl.demo.servlet.filter;

import acl.demo.entity.UserSession;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

public class SecurityFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        HttpSession session = request.getSession(false);

        System.out.println("[SecurityFilter] ServletPath : " + request.getServletPath());
        if(!request.getServletPath().equalsIgnoreCase("/login") &&
                !request.getServletPath().equalsIgnoreCase("/logout") &&
                !request.getServletPath().equalsIgnoreCase("/loginForm.jsp") &&
                !request.getServletPath().equalsIgnoreCase("/css/format.css")) {
            if(session != null) {
                UserSession userSession = (UserSession) session.getAttribute("userSession");

                if(userSession == null) {
                    System.out.println("[SecurityFilter] userSession : " + userSession);
                    request.setAttribute("message", "Please login first!");
                    request.getRequestDispatcher("/loginForm.jsp").forward(request, response);
                    return;
                }
            } else {
                request.setAttribute("message", "Please login!");
                request.getRequestDispatcher("/loginForm.jsp").forward(request, response);
                return;
            }
        }

        chain.doFilter(req, resp);
    }
}

This is a simple example of an Access Control List (ACL) presented in Java, you can dowload it from here and here

Servlet Filter can be used to preprocess Web application requests, therefore we can used it to secure our websites. If your web application need someone to login first before he/she can browse to another page, servlet filter can be used in such cases. Below will show how to use servlet filter to secure a web application.

Servlet filter is an interface on package javax.servlet.Filter that have three methods: init, doFilter, and destroy. We have to make our own filter class and implements Filter interface. This is the filter class called HelloFilter:

(com.halimun.filter.HelloFilter.java)

package com.halimun.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class HelloFilter implements Filter {
	private FilterConfig filterConfig;
	private String loginForm;
	
	// init
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
		loginForm = this.filterConfig.getInitParameter("login_form");
	}
	
	// doFilter
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpSession session = httpRequest.getSession(false);
		
		if( session != null ) {
			String currentUser = (String)session.getAttribute("user");
			if (currentUser == null) {
				System.out.println("currentUser null");
				filterConfig.getServletContext().getRequestDispatcher(loginForm).forward(request, response);
			}
		
		}
		else{
			filterConfig.getServletContext().getRequestDispatcher(loginForm).forward(request, response);
		}

		chain.doFilter(request,response);

	}

	// destroy
	public void destroy() {}

}

The UserLogin action servlet is invoke when user press submit button at login form. It’s check wether user is exist in databases and match the password. Acctually it’s only dummy database, I use HashMap to store username (as the key) and password.

(com.halimun.servlet.UserLogin)

package com.halimun.servlet;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UserLogin extends HttpServlet {
	private static final long serialVersionUID = -3955012280873977969L;
	private static final Map<String, String> users = new HashMap<String, String>();
	
	public void init() throws ServletException {
		users.put("admin", "secret");
		users.put("david", "password");
		users.put("gardiary", "gardiary");
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException 
	{
		execute(request, response);
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		execute(request, response);
	}
	
	private void execute(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException 
	{
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		String userPassword = users.get(username);
		
		if(userPassword!=null && userPassword.equals(password)) {
			request.getSession().setAttribute("user", username);
			response.sendRedirect( request.getContextPath() );
		}
		else {
			request.setAttribute("message", "Invalid username or password");
			getServletContext().getRequestDispatcher("/loginForm.jsp")
				.forward(request, response);
		}
	}
}

UserLogout action servlet is for logout from the application, it’s remove the session and redirect to login form.

(com.halimun.servlet.UserLogout.java)

package com.halimun.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UserLogout extends HttpServlet {
	private static final long serialVersionUID = 5073946739765619794L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException 
	{
		execute(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException
	{
		execute(request, response);
	}

	private void execute(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException 
	{
		request.getSession().invalidate();
		response.sendRedirect( request.getContextPath() );
	}
}

Here is login form using JSP. We can see that the form action is “userlogin.action” which is a UserLogin action servlet.

(loginForm.jsp)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="css/format.css" rel=stylesheet type="text/css">
<title>Login Form</title>
</head>
<body>
<form method="POST" action="userlogin.action">
	<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse">
		<tr>
			<td>Username :</td>
			<td><input type="text" name="username"></td>
		</tr>
		<tr>
			<td>Password :</td>
			<td><input type="password" name="password"></td>
		</tr>
		<tr>
			<td>&nbsp</td>
			<td><b>
				<%= (request.getAttribute("message")==null ? "&nbsp" : request.getAttribute("message")) %>
			</b></td>
		</tr>
		<tr>
			<td colspan="2" align="center">
				<input type="submit" value="Login">
				<input type="reset" value="Cancel">
			</td>
		</tr>
   </table>
</form>
<br>
<a href="index.jsp">index page</a> - <a href="error.jsp">error page</a> - 
<a href="dummy.jsp">dummy page</a>
</body>
</html>

Index.jsp is default page after user has succeed login. You can make another pages to check if the security is working.

(index.jsp)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="css/format.css" rel=stylesheet type="text/css">
<title>Index Page</title>
</head>
<body>
	<h2>Heloooo <%= session.getAttribute("user") %>...</h2>
	<br>
	<a href="error.jsp">error page</a> - <a href="dummy.jsp">dummy page</a> - 
	<a href="loginForm.jsp">login form</a>- <a href="userlogout.action">logout</a>
</body>
</html>

And here is the web.xml:

<?xml version="1.0" encoding="UTF-8"?>

<web-app>
	<description>Web Application Security</description>
	<display-name>Web Application Security</display-name>

	<filter>
		<filter-name>HelloFilter</filter-name>
		<filter-class>com.halimun.filter.HelloFilter</filter-class>
		<description>
			This Is Hello Filter
		</description>
		<init-param>
			<param-name>login_form</param-name>
			<param-value>/loginForm.jsp</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>HelloFilter</filter-name>
		<url-pattern>*.jsp</url-pattern>
	</filter-mapping>
	
	<servlet>
		<servlet-name>userlogin</servlet-name>
		<description>User Login Controller</description>
		<servlet-class>com.halimun.servlet.UserLogin</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>userlogout</servlet-name>
		<description>User Logout Controller</description>
		<servlet-class>com.halimun.servlet.UserLogout</servlet-class>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>userlogin</servlet-name>
		<url-pattern>/userlogin.action</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>userlogout</servlet-name>
		<url-pattern>/userlogout.action</url-pattern>
	</servlet-mapping>
	
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

<filter-mapping>
   <filter-name>HelloFilter</filter-name>
   <url-pattern>*.jsp</url-pattern>
</filter-mapping>

This mapping means that request to every JSP file will be filtered first, in this case will invoke HelloFilter filter. HelloFilter filter will be processes before and after run the JSP’s files.
Acctually we can mapped filter to every requests by specifying “/*” in url-pattern of filter-mapping. In that case, every request to every resources (such as *.action, *,css, *.js, etc) will also invoke the filter.

<servlet-mapping>
   <servlet-name>userlogin</servlet-name>
   <url-pattern>/userlogin.action</url-pattern>
</servlet-mapping>
<servlet-mapping>
   <servlet-name>userlogout</servlet-name>
   <url-pattern>/userlogout.action</url-pattern>
</servlet-mapping>

In this servlet mappings, we mapped UserLogin action servlet with url-pattern “/userlogin.action” and UserLogout action servlet with url-pattern “/userlogout.action“.

Summary

So the web application structure will look like this:

[ContextRoot]\index.jsp
[ContextRoot]\loginForm.jsp
[ContextRoot]\error.jsp
[ContextRoot]\dummy.jsp
[ContextRoot]\css\format.css
[ContextRoot]\WEB-INF
[ContextRoot]\WEB-INF\classes\com\halimun\filter\HelloFilter.class
[ContextRoot]\WEB-INF\classes\com\halimun\servlet\UserLogin.class
[ContextRoot]\WEB-INF\classes\com\halimun\servlet\UserLogout.class

Complete source code is this, change the extention to .war (coz free wordpress doesnt support WAR files 😦 ), then deploy in your favorite application server. Try to access error page or dummy page without entering username/password in login form. For login account, you can use admin/secret, david/password, or gardiary/gardiary.