openplanning

Tạo một ứng dụng Login đơn giản và bảo mật với Java Servlet Filter

  1. Tại sao bảo mật?
  2. Khái niệm về Role và Principal
  3. Bảo mật với Servlet Filter
  4. Mục tiêu của ví dụ
  5. Tạo ứng dụng
  6. UserAccount & DataDAO
  7. SecurityConfig & SecurityUtils
  8. SecurityFilter
  9. Trang chủ, Login, Logout
  10. Các trang bắt buộc phải đăng nhập

1. Tại sao bảo mật?

Bảo mật (Security) là môt khía cạnh quan trọng của một ứng dụng có sự vận chuyển các dữ liệu quan trọng trên Internet.
Authentication (Xác thực)
Xác thực là quá trình mà quyền truy cập (access privileges) của người dùng được xác minh trước khi họ vào khu vực được bảo vệ của Website. Có hai cách tiếp cận xác thực chính: xác thực cơ bản và xác thực dựa trên biểu mẫu (Form-based authentication).
Basic Authentication (Xác thực cơ bản).
Với xác thực cơ bản, người dùng có thể truy cập mọi trang (page) hoàn toàn bình thường, với các trang yêu cầu bảo mật, một cửa sổ sẽ hiển thị để người dùng nhập vào username/password của họ. Thông tin username/password sẽ được gói lại gửi kèm theo yêu cầu (request) đến Server.
Khi người dùng nhập một đường dẫn trên trình duyệt, và nhấn Enter để yêu cầu một trang (page). Một thông tin "User Agent" được tạo ra và được gửi kèm theo yêu cầu. Thông thường thông tin này bao gồm thông tin trình duyệt của người dùng, thông tin hệ điều hành. Trong trường hợp xác thực cơ bản (basic authentication) thông tin username/password được gói bên trong "User Agent".
Trong bài học này tôi không đề cập chi tiết về xác thực cơ bản.
Form-based Authentication (Xác thực dựa trên biểu mẫu)
Hầu hết các Website sử dụng hình thức xác thực dựa trên biểu mẫu (Form-based Authentication). Website cho phép người dùng truy cập mọi trang thông thường mà không yêu cầu mật khẩu. Tuy nhiên nếu người dùng truy cập vào một trang được bảo vệ, nó sẽ chuyển hướng tới một trang đăng nhập.
Trong bài học này, tôi sẽ đề cập chi tiết về cách sử dụng một Servlet Filter để bảo mật ứng dụng Java Web.

2. Khái niệm về Role và Principal

Trong bảo mật, có 2 khái niệm quan trọng là Principal Role.
Role
Role (Vai trò) là một tập hợp các quyền (permission) đối với một ứng dụng.
Để đơn giản tôi đưa ra một ví dụ, ứng dụng ABC có 2 vai trò "EMPLOYEE" (nhân viên) và "MANAGER" (Người quản lý).
  • Vai trò "EMPLOYEE" được phép sử dụng các chức năng bán hàng, và chức năng tạo mới thông tin khách hàng.
  • Vai trò "MANAGER" được phép sử dụng các chức năng quản lý nhân viên, và xem các báo cáo doanh thu.
Principal
Principal có thể tạm hiểu là một "Chủ thể" sau khi đã đăng nhập vào một hệ thống, họ có quyền làm điều gì đó trong hệ thống. Một "Chủ thể" có thể có một hoặc nhiều vai trò. Điều này phụ thuộc vào sự phân quyền của ứng dụng cho mỗi tài khoản người dùng khác nhau.

3. Bảo mật với Servlet Filter

Trong ứng dụng Java Servlet, một Servlet Filter đặc biệt được sử dụng để xử lý bảo mật, nó thường được gọi là Security Filter.
Dưới đây là nguyên tắc hoạt động của Security Filter.
Khi người dùng truy cập vào một trang (page) được bảo vệ, Security Filter sẽ kiểm tra, nếu người dùng chưa đăng nhập, yêu cầu của người dùng sẽ bị chuyển hướng (redirect) sang trang đăng nhập.
Nếu người dùng đã đăng nhập thành công, một đối tượng Principal được tạo ra, nó mang các thông tin của người dùng, bao gồm cả các vai trò.
Nếu người dùng đã đăng nhập thành công trước đó, và truy cập vào một trang (page) được bảo vệ. Security Filter sẽ kiểm tra các vai trò của người dùng có phù hợp để truy cập vào trang này hay không. Nếu không hợp lệ, nó sẽ hiển thị cho người dùng một trang thông báo truy cập bị cấm (Access Denied).

4. Mục tiêu của ví dụ

Đây là cấu trúc của ứng dụng mà chúng ta sẽ thực hiện:
Ứng dụng bao gồm 2 vai trò (Role) là EMPLOYEE MANAGER.
  1. Vai trò EMPLOYEE cho phép truy cập 2 trang /userInfo/employeeTask
  2. Vai trò MANAGER cho phép truy cập 2 trang /userInfo/managerTask.
Tất cả các trang khác trong ứng dụng không yêu cầu phải đăng nhập.
Có 2 người dùng (user) là employee1manager1.
  • Người dùng employee1 được gán vai trò EMPLOYEE
  • Người dùng manager1 được gán cả 2 vai trò MANAGER EMPLOYEE.
Người dùng có thể truy cập tất cả các trang không được bảo hộ một cách bình thường. Tuy nhiên nếu người dùng truy cập vào một trang ABC được bảo hộ, nó sẽ chuyển hướng (redirect) tới trang đăng nhập. Người dùng đăng nhập thành công, lúc này sẽ có 2 tình huống xẩy ra:
  • Ứng dụng sẽ chuyển hướng về trang ABC sau khi đăng nhập thành công, nếu userName có vai trò phù hợp.
  • Ứng dụng sẽ hiển thị thông báo truy cập bị từ chối (Access Denied) nếu userName có vai trò không phù hợp.

5. Tạo ứng dụng

Trên Eclipse tạo một "Dynamic Web Project" có tên SecurityWebApp, và cấu hình để nó có thể chạy trên Tomcat Server. Tôi sẽ không đề cập chi tiết về các bước để thực hiện điều này, bạn có thể tham khảo thêm trong bài học dưới đây:

6. UserAccount & DataDAO

Lớp UserAccount đại diện cho người dùng của ứng dụng.
UserAccount.java
package org.o7planning.securitywebapp.bean;

import java.util.ArrayList;
import java.util.List;

public class UserAccount {
   public static final String GENDER_MALE = "M";
   public static final String GENDER_FEMALE = "F";

   private String userName;
   private String gender;
   private String password;

   private List<String> roles;

   public UserAccount() {

   }

   public UserAccount(String userName, String password, String gender, String... roles) {
      this.userName = userName;
      this.password = password;
      this.gender = gender;

      this.roles = new ArrayList<String>();
      if (roles != null) {
         for (String r : roles) {
            this.roles.add(r);
         }
      }
   }

   public String getUserName() {
      return userName;
   }

   public void setUserName(String userName) {
      this.userName = userName;
   }

   public String getGender() {
      return gender;
   }

   public void setGender(String gender) {
      this.gender = gender;
   }

   public String getPassword() {
      return password;
   }

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

   public List<String> getRoles() {
      return roles;
   }

   public void setRoles(List<String> roles) {
      this.roles = roles;
   }
}
Lớp DataDAO được sử dụng để truy vấn dữ liệu trong Database (Mô phỏng).
DataDAO.java
package org.o7planning.securitywebapp.utils;

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

import org.o7planning.securitywebapp.bean.UserAccount;
import org.o7planning.securitywebapp.config.SecurityConfig;

public class DataDAO {

	private static final Map<String, UserAccount> mapUsers = new HashMap<String, UserAccount>();

	static {
		initUsers();
	}

	private static void initUsers() {

		// User này có 1 vai trò là EMPLOYEE.
		UserAccount emp = new UserAccount("employee1", "123", UserAccount.GENDER_MALE, //
				SecurityConfig.ROLE_EMPLOYEE);

		// User này có 2 vai trò EMPLOYEE và MANAGER.
		UserAccount mng = new UserAccount("manager1", "123", UserAccount.GENDER_MALE, //
				SecurityConfig.ROLE_EMPLOYEE, SecurityConfig.ROLE_MANAGER);

		mapUsers.put(emp.getUserName(), emp);
		mapUsers.put(mng.getUserName(), mng);
	}

	// Tìm kiếm người dùng theo userName và password.
	public static UserAccount findUser(String userName, String password) {
		UserAccount u = mapUsers.get(userName);
		if (u != null && u.getPassword().equals(password)) {
			return u;
		}
		return null;
	}

}

7. SecurityConfig & SecurityUtils

Lớp SecurityConfig giúp cấu hình các vai trò và các chức năng (các trang) được phép truy cập ứng với vai trò đó.
SecurityConfig.java
package org.o7planning.securitywebapp.config;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SecurityConfig {

	public static final String ROLE_MANAGER = "MANAGER";
	public static final String ROLE_EMPLOYEE = "EMPLOYEE";

	// String: Role
	// List<String>: urlPatterns.
	private static final Map<String, List<String>> mapConfig = new HashMap<String, List<String>>();

	static {
		init();
	}

	private static void init() {

		// Cấu hình cho vai trò "EMPLOYEE".
		List<String> urlPatterns1 = new ArrayList<String>();

		urlPatterns1.add("/userInfo");
		urlPatterns1.add("/employeeTask");

		mapConfig.put(ROLE_EMPLOYEE, urlPatterns1);

		// Cấu hình cho vai trò "MANAGER".
		List<String> urlPatterns2 = new ArrayList<String>();

		urlPatterns2.add("/userInfo");
		urlPatterns2.add("/managerTask");

		mapConfig.put(ROLE_MANAGER, urlPatterns2);
	}

	public static Set<String> getAllAppRoles() {
		return mapConfig.keySet();
	}

	public static List<String> getUrlPatternsForRole(String role) {
		return mapConfig.get(role);
	}

}
Lớp SecurityUtils là một lớp tiện ích, nó có các phương thức giúp kiểm tra một request (yêu cầu) có bắt buộc phải đăng nhập hay không, và request đó có phù hợp với vai trò của người dùng đã đăng nhập hay không.
SecurityUtils.java
package org.o7planning.securitywebapp.utils;

import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.o7planning.securitywebapp.config.SecurityConfig;

public class SecurityUtils {

	// Kiểm tra 'request' này có bắt buộc phải đăng nhập hay không.
	public static boolean isSecurityPage(HttpServletRequest request) {
		String urlPattern = UrlPatternUtils.getUrlPattern(request);

		Set<String> roles = SecurityConfig.getAllAppRoles();

		for (String role : roles) {
			List<String> urlPatterns = SecurityConfig.getUrlPatternsForRole(role);
			if (urlPatterns != null && urlPatterns.contains(urlPattern)) {
				return true;
			}
		}
		return false;
	}

	// Kiểm tra 'request' này có vai trò phù hợp hay không?
	public static boolean hasPermission(HttpServletRequest request) {
		String urlPattern = UrlPatternUtils.getUrlPattern(request);

		Set<String> allRoles = SecurityConfig.getAllAppRoles();

		for (String role : allRoles) {
			if (!request.isUserInRole(role)) {
				continue;
			}
			List<String> urlPatterns = SecurityConfig.getUrlPatternsForRole(role);
			if (urlPatterns != null && urlPatterns.contains(urlPattern)) {
				return true;
			}
		}
		return false;
	}
}
UrlPatternUtils.java
package org.o7planning.securitywebapp.utils;

import java.util.Collection;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServletRequest;

public class UrlPatternUtils {

   private static boolean hasUrlPattern(ServletContext servletContext, String urlPattern) {

      Map<String, ? extends ServletRegistration> map = servletContext.getServletRegistrations();

      for (String servletName : map.keySet()) {
         ServletRegistration sr = map.get(servletName);

         Collection<String> mappings = sr.getMappings();
         if (mappings.contains(urlPattern)) {
            return true;
         }

      }
      return false;
   }

   // servletPath:
   // ==> /spath
   // ==> /spath/*
   // ==> *.ext
   // ==> /
   public static String getUrlPattern(HttpServletRequest request) {
      ServletContext servletContext = request.getServletContext();
      String servletPath = request.getServletPath();
      String pathInfo = request.getPathInfo();

      String urlPattern = null;
      if (pathInfo != null) {
         urlPattern = servletPath + "/*";
         return urlPattern;
      }
      urlPattern = servletPath;

      boolean has = hasUrlPattern(servletContext, urlPattern);
      if (has) {
         return urlPattern;
      }
      int i = servletPath.lastIndexOf('.');
      if (i != -1) {
         String ext = servletPath.substring(i + 1);
         urlPattern = "*." + ext;
         has = hasUrlPattern(servletContext, urlPattern);

         if (has) {
            return urlPattern;
         }
      }
      return "/";
   }
}

8. SecurityFilter

SecurityFilter là một Servlet Filter, nó làm nhiệm vụ kiểm tra các request trước khi cho phép truy cập vào các trang (page) được bảo hộ.

SecurityFilter đọc "các cấu hình bảo mật" đã được khai báo trong lớp SecurityConfig.
SecurityFilter.java
package org.o7planning.securitywebapp.filter;

import java.io.IOException;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.o7planning.securitywebapp.bean.UserAccount;
import org.o7planning.securitywebapp.request.UserRoleRequestWrapper;
import org.o7planning.securitywebapp.utils.AppUtils;
import org.o7planning.securitywebapp.utils.SecurityUtils;

@WebFilter("/*")
public class SecurityFilter implements Filter {

	public SecurityFilter() {
	}

	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		String servletPath = request.getServletPath();

		// Thông tin người dùng đã được lưu trong Session
		// (Sau khi đăng nhập thành công).
		UserAccount loginedUser = AppUtils.getLoginedUser(request.getSession());

		if (servletPath.equals("/login")) {
			chain.doFilter(request, response);
			return;
		}
		HttpServletRequest wrapRequest = request;

		if (loginedUser != null) {
			// User Name
			String userName = loginedUser.getUserName();

			// Các vai trò (Role).
			List<String> roles = loginedUser.getRoles();

			// Gói request cũ bởi một Request mới với các thông tin userName và Roles.
			wrapRequest = new UserRoleRequestWrapper(userName, roles, request);
		}

		// Các trang bắt buộc phải đăng nhập.
		if (SecurityUtils.isSecurityPage(request)) {

			// Nếu người dùng chưa đăng nhập,
			// Redirect (chuyển hướng) tới trang đăng nhập.
			if (loginedUser == null) {

				String requestUri = request.getRequestURI();

				// Lưu trữ trang hiện tại để redirect đến sau khi đăng nhập thành công.
				int redirectId = AppUtils.storeRedirectAfterLoginUrl(request.getSession(), requestUri);

				response.sendRedirect(wrapRequest.getContextPath() + "/login?redirectId=" + redirectId);
				return;
			}

			// Kiểm tra người dùng có vai trò hợp lệ hay không?
			boolean hasPermission = SecurityUtils.hasPermission(wrapRequest);
			if (!hasPermission) {

				RequestDispatcher dispatcher //
						= request.getServletContext().getRequestDispatcher("/WEB-INF/views/accessDeniedView.jsp");

				dispatcher.forward(request, response);
				return;
			}
		}

		chain.doFilter(wrapRequest, response);
	}

	@Override
	public void init(FilterConfig fConfig) throws ServletException {

	}

}
UserRoleRequestWrapper.java
package org.o7planning.securitywebapp.request;

import java.security.Principal;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * An extension for the HTTPServletRequest that overrides the getUserPrincipal()
 * and isUserInRole(). We supply these implementations here, where they are not
 * normally populated unless we are going through the facility provided by the
 * container.
 * <p>
 * If he user or roles are null on this wrapper, the parent request is consulted
 * to try to fetch what ever the container has set for us. This is intended to
 * be created and used by the UserRoleFilter.
 * 
 * @author thein
 *
 */
public class UserRoleRequestWrapper extends HttpServletRequestWrapper {

	private String user;
	private List<String> roles = null;
	private HttpServletRequest realRequest;

	public UserRoleRequestWrapper(String user, List<String> roles, HttpServletRequest request) {
		super(request);
		this.user = user;
		this.roles = roles;
		this.realRequest = request;
	}

	@Override
	public boolean isUserInRole(String role) {
		if (roles == null) {
			return this.realRequest.isUserInRole(role);
		}
		return roles.contains(role);
	}

	@Override
	public Principal getUserPrincipal() {
		if (this.user == null) {
			return realRequest.getUserPrincipal();
		}

		// Make an anonymous implementation to just return our user
		return new Principal() {
			@Override
			public String getName() {
				return user;
			}
		};
	}
}

9. Trang chủ, Login, Logout

HomeServlet.java
package org.o7planning.securitywebapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet({ "/", "/index" })
public class HomeServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public HomeServlet() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        RequestDispatcher dispatcher //
                = this.getServletContext().getRequestDispatcher("/WEB-INF/views/homeView.jsp");

        dispatcher.forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        doGet(request, response);
    }

}
LoginServlet.java
package org.o7planning.securitywebapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.o7planning.securitywebapp.bean.UserAccount;
import org.o7planning.securitywebapp.utils.AppUtils;
import org.o7planning.securitywebapp.utils.DataDAO;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public LoginServlet() {
		super();
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		RequestDispatcher dispatcher //
				= this.getServletContext().getRequestDispatcher("/WEB-INF/views/loginView.jsp");

		dispatcher.forward(request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String userName = request.getParameter("userName");
		String password = request.getParameter("password");
		UserAccount userAccount = DataDAO.findUser(userName, password);

		if (userAccount == null) {
			String errorMessage = "Invalid userName or Password";

			request.setAttribute("errorMessage", errorMessage);

			RequestDispatcher dispatcher //
					= this.getServletContext().getRequestDispatcher("/WEB-INF/views/loginView.jsp");

			dispatcher.forward(request, response);
			return;
		}

		AppUtils.storeLoginedUser(request.getSession(), userAccount);

		// 
		int redirectId = -1;
		try {
			redirectId = Integer.parseInt(request.getParameter("redirectId"));
		} catch (Exception e) {
		}
		String requestUri = AppUtils.getRedirectAfterLoginUrl(request.getSession(), redirectId);
		if (requestUri != null) {
			response.sendRedirect(requestUri);
		} else {
			// Mặc định sau khi đăng nhập thành công
			// chuyển hướng về trang /userInfo
			response.sendRedirect(request.getContextPath() + "/userInfo");
		}

	}

}
AppUtils.java
package org.o7planning.securitywebapp.utils;

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

import javax.servlet.http.HttpSession;

import org.o7planning.securitywebapp.bean.UserAccount;

public class AppUtils {

	private static int REDIRECT_ID = 0;

	private static final Map<Integer, String> id_uri_map = new HashMap<Integer, String>();
	private static final Map<String, Integer> uri_id_map = new HashMap<String, Integer>();

	// Lưu trữ thông tin người dùng vào Session.
	public static void storeLoginedUser(HttpSession session, UserAccount loginedUser) {
		// Trên JSP có thể truy cập thông qua ${loginedUser}
		session.setAttribute("loginedUser", loginedUser);
	}

	// Lấy thông tin người dùng lưu trữ trong Session.
	public static UserAccount getLoginedUser(HttpSession session) {
		UserAccount loginedUser = (UserAccount) session.getAttribute("loginedUser");
		return loginedUser;
	}

	public static int storeRedirectAfterLoginUrl(HttpSession session, String requestUri) {
		Integer id = uri_id_map.get(requestUri);

		if (id == null) {
			id = REDIRECT_ID++;

			uri_id_map.put(requestUri, id);
			id_uri_map.put(id, requestUri);
			return id;
		}

		return id;
	}

	public static String getRedirectAfterLoginUrl(HttpSession session, int redirectId) {
		String url = id_uri_map.get(redirectId);
		if (url != null) {
			return url;
		}
		return null;
	}

}
LogoutServlet.java
package org.o7planning.securitywebapp.servlet;

import java.io.IOException;

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

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public LogoutServlet() {
		super();
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.getSession().invalidate();

		// Redrect to Home Page.
		response.sendRedirect(request.getContextPath() + "/");

	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		this.doGet(request, response);
	}

}
UserInfoServlet.java
package org.o7planning.securitywebapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {
   private static final long serialVersionUID = 1L;

   public UserInfoServlet() {
      super();
   }

   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {

      RequestDispatcher dispatcher //
            = this.getServletContext().getRequestDispatcher("/WEB-INF/views/userInfoView.jsp");

      dispatcher.forward(request, response);
   }

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

}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://java.sun.com/xml/ns/javaee"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      id="WebApp_ID" version="3.0">
      
  <display-name>SecurityWebApp</display-name>
 
 
  <welcome-file-list>
 
    <welcome-file>/</welcome-file>
     
  </welcome-file-list>
 
 
</web-app>
/WEB-INF/views/_menu.jsp
<a href="${pageContext.request.contextPath}/employeeTask">
  Employee Task
</a>
||
<a href="${pageContext.request.contextPath}/managerTask">
  Manager Task
</a>
||
<a href="${pageContext.request.contextPath}/userInfo">
  User Info
</a>       
||
<a href="${pageContext.request.contextPath}/login">
  Login
</a>
||
<a href="${pageContext.request.contextPath}/logout">
  Logout
</a>

&nbsp;
<span style="color:red">[ ${loginedUser.userName} ]</span>
/WEB-INF/views/homeView.jsp
<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <title>Home Page</title>
   </head>
   <body>
   
      <jsp:include page="_menu.jsp"></jsp:include>
   
      <h3>Home Page</h3>
            
   </body>
</html>
/WEB-INF/views/loginView.jsp
<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <title>Login</title>
   </head>
   <body>

      <jsp:include page="_menu.jsp"></jsp:include>   

      <h3>Login Page</h3>

      <p style="color: red;">${errorString}</p>

      <form method="POST" action="${pageContext.request.contextPath}/login">
         <input type="hidden" name="redirectId" value="${param.redirectId}" />
         <table border="0">
            <tr>
               <td>User Name</td>
               <td><input type="text" name="userName" value= "${user.userName}" /> </td>
            </tr>
            <tr>
               <td>Password</td>
               <td><input type="password" name="password" value= "${user.password}" /> </td>
            </tr>
         
            <tr>
               <td colspan ="2">
                  <input type="submit" value= "Submit" />
                  <a href="${pageContext.request.contextPath}/">Cancel</a>
               </td>
            </tr>
         </table>
      </form>

      <p style="color:blue;">Login with:</p>
      
      employee1/123 <br>
      manager1/123
     
 

   </body>
</html>
/WEB-INF/views/userInfoView.jsp
<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <title>User Info</title>
   </head>
   <body>

     <jsp:include page="_menu.jsp"></jsp:include>

      <h3>Hello: ${loginedUser.userName}</h3>

      User Name: <b>${loginedUser.userName}</b>
      <br />
      Gender: ${loginedUser.gender } <br />


   </body>
</html>
Chạy ứng dụng:

10. Các trang bắt buộc phải đăng nhập

EmployeeTaskServlet.java
package org.o7planning.securitywebapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/employeeTask")
public class EmployeeTaskServlet extends HttpServlet {
   private static final long serialVersionUID = 1L;

   public EmployeeTaskServlet() {
      super();
   }

   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {

      RequestDispatcher dispatcher //
            = this.getServletContext()//
                  .getRequestDispatcher("/WEB-INF/views/employeeTaskView.jsp");

      dispatcher.forward(request, response);
   }

   @Override
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {

      doGet(request, response);
   }

}
ManagerTaskServlet.java
package org.o7planning.securitywebapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/managerTask")
public class ManagerTaskServlet extends HttpServlet {
   private static final long serialVersionUID = 1L;

   public ManagerTaskServlet() {
      super();
   }

   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {

      RequestDispatcher dispatcher //
            = this.getServletContext()//
                  .getRequestDispatcher("/WEB-INF/views/managerTaskView.jsp");

      dispatcher.forward(request, response);
   }

   @Override
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {

      doGet(request, response);
   }

}
/WEB-INF/views/employeeTaskView.jsp
<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <title>Employee Task</title>
   </head>
   <body>
  
      <jsp:include page="_menu.jsp"></jsp:include>
      
      <h3>Employee Task</h3>
      
      Hello, This is a protected page!
      
   </body>
</html>
/WEB-INF/views/managerTaskView.jsp
<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <title>Manager Task</title>
   </head>
   
   <body>
   
      <jsp:include page="_menu.jsp"></jsp:include>
      
      <h3>Manager Task</h3>
      
      Hello, This is a protected page!
      
   </body>
</html>
/WEB-INF/views/accessDenied.jsp
<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <title>Access Denied</title>
   </head>
   <body>
   
      <jsp:include page="_menu.jsp"></jsp:include>
   
      <br/><br/>
      
      <h3 style="color:red;">Access Denied!</h3>
            
   </body>
</html>
Chạy ứng dụng:
Chạy ứng dụng và đăng nhập với userName = "employee1", đây là người dùng có vai trò "EMPLOYEE".
Chạy ứng dụng và đăng nhập với userName = "manager1", đây là người dùng có 2 vai trò "EMPLOYEE""MANAGER".