Cơ chế xác thực đăng nhập giúp người dùng truy cập ứng dụng một cách an toàn thông qua việc xác thực username và password.
Trong bài này, ta sẽ sử dụng JSF view cho trang đăng nhập, đối tượng DAO, HttpSession để quản lý session, bean do JSF quản lý (managed bean) và cơ sở dữ liệu MySQL. Hãy cùng xem chi tiết cách tạo một cơ chế xác thực người dùng với JSF.

Bước 1: Tạo bảng Users trong database MySQL như sau:
CREATE TABLE Users(
uid int(20) NOT NULL AUTO_INCREMENT,
uname VARCHAR(60) NOT NULL,
password VARCHAR(60) NOT NULL,
PRIMARY KEY(uid));
Bảng này có uid là khóa chính, cùng hai trường username và password với ràng buộc not null.
Bước 2: Tiếp theo, chèn một vài dữ liệu vào bảng Users:
INSERT INTO Users VALUES(1,'adam','adam');
Trước khi đi vào phần code của project ví dụ, hãy xem qua cấu trúc của nó trong Eclipse ở hình dưới. Bạn chỉ cần tạo một project web động và chuyển đổi nó sang Maven để có một sườn ban đầu. Sau đó ta sẽ thêm dần các thành phần khác vào.

Bước 3: Tạo trang đăng nhập JSF login.xhtml như sau:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "<https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>">
<html xmlns="<https://www.w3.org/1999/xhtml>"
xmlns:h="<https://java.sun.com/jsf/html>">
<h:head>
<title>login</title>
</h:head>
<h:body>
<h:form>
<h3>JSF Login Logout</h3>
<h:outputText value="Username" />
<h:inputText id="username" value="#{login.user}"></h:inputText>
<h:message for="username"></h:message>
<br></br><br></br>
<h:outputText value="Password" />
<h:inputSecret id="password" value="#{login.pwd}"></h:inputSecret>
<h:message for="password"></h:message>
<br></br><br></br>
<h:commandButton action="#{login.validateUsernamePassword}"
value="Login"></h:commandButton>
</h:form>
</h:body>
Ở đây, ta tạo một trang view JSF cho việc đăng nhập, bao gồm các trường username và password. Giá trị của các trường này được thiết lập thông qua login managed bean. Khi người dùng nhấn nút Login, ta sẽ gọi phương thức validateUsernamePassword để xác thực username và password.
Bước 4: Tạo managed bean trong file Login.java như sau:
package com.journaldev.jsf.beans;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSession;
import com.journaldev.jsf.dao.LoginDAO;
import com.journaldev.jsf.util.SessionUtils;
@ManagedBean
@SessionScoped
public class Login implements Serializable {
private static final long serialVersionUID = 1094801825228386363L;
private String pwd;
private String msg;
private String user;
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
//validate login
public String validateUsernamePassword() {
boolean valid = LoginDAO.validate(user, pwd);
if (valid) {
HttpSession session = SessionUtils.getSession();
session.setAttribute("username", user);
return "admin";
} else {
FacesContext.getCurrentInstance().addMessage(
null,
new FacesMessage(FacesMessage.SEVERITY_WARN,
"Incorrect Username and Passowrd",
"Please enter correct username and Password"));
return "login";
}
}
//logout event, invalidate session
public String logout() {
HttpSession session = SessionUtils.getSession();
session.invalidate();
return "login";
}
}
Ta khai báo ba biến String là user, pwd và msg tương ứng cho username, password và thông báo lỗi, kèm theo các phương thức getter và setter (các phương thức dùng để thức lấy và đặt các giá trị).
Phương thức validateUsernamePassword() được dùng để xác thực username và password. Nó sẽ gọi class LoginDAO để lấy thông tin từ database rồi so sánh với giá trị người dùng nhập vào ở giao diện.
Nếu username và password không khớp, một thông báo lỗi “Incorrect username and password” sẽ được hiển thị. Ngoài ra, ta còn có phương thức logout để thực hiện việc đăng xuất bằng cách vô hiệu hóa HttpSession hiện tại.
Bước 5: Bây giờ, hãy tạo class Java LoginDAO như dưới đây. Lưu ý rằng code xử lý database chưa được tối ưu để dùng trong một dự án thực tế. Ở đây ta viết nó một cách nhanh nhất có thể vì mục đích chính là để học về xác thực trong ứng dụng JSF.
package com.journaldev.jsf.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.journaldev.jsf.util.DataConnect;
public class LoginDAO {
public static boolean validate(String user, String password) {
Connection con = null;
PreparedStatement ps = null;
try {
con = DataConnect.getConnection();
ps = con.prepareStatement("Select uname, password from Users where uname = ? and password = ?");
ps.setString(1, user);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
//result found, means valid inputs
return true;
}
} catch (SQLException ex) {
System.out.println("Login error -->" + ex.getMessage());
return false;
} finally {
DataConnect.close(con);
}
return false;
}
}
Trong phương thức validate(), đầu tiên ta thiết lập kết nối tới database bằng cách gọi phương thức getConnection của class DataConnect. Ta sử dụng PreparedStatement để xây dựng câu truy vấn (quey) lấy dữ liệu từ database dựa trên giá trị người dùng nhập vào. Nếu kết quả trả về có dữ liệu, nghĩa là thông tin hợp lệ, phương thức sẽ trả về true (ngược lại trả về false).
Bước 6: Tạo class DataConnect.java như sau:
package com.journaldev.jsf.util;
import java.sql.Connection;
import java.sql.DriverManager;
public class DataConnect {
public static Connection getConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/cardb", "pankaj", "pankaj123");
return con;
} catch (Exception ex) {
System.out.println("Database.getConnection() Error -->"
+ ex.getMessage());
return null;
}
}
public static void close(Connection con) {
try {
con.close();
} catch (Exception ex) {
}
}
}
Ta nạp JDBC driver bằng phương thức Class.forName() và sử dụng phương thức DriverManager.getConnection() để kết nối đến database và truyền vào các tham số url, username và password.
Bước 7: Tạo SessionUtils.java để lấy và quản lý thông tin người dùng trong session.
package com.journaldev.jsf.beans;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class SessionUtils {
public static HttpSession getSession() {
return (HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false);
}
public static HttpServletRequest getRequest() {
return (HttpServletRequest) FacesContext.getCurrentInstance()
.getExternalContext().getRequest();
}
public static String getUserName() {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false);
return session.getAttribute("username").toString();
}
public static String getUserId() {
HttpSession session = getSession();
if (session != null)
return (String) session.getAttribute("userid");
else
return null;
}
}
Class này giúp ta lấy session của người dùng đã đăng nhập, và từ đó có thể lấy các thông tin như userId đã được liên kết với session đó.
Bước 8: Tạo class lọc như sau:
package com.journaldev.jsf.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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebFilter(filterName = "AuthFilter", urlPatterns = { "*.xhtml" })
public class AuthorizationFilter implements Filter {
public AuthorizationFilter() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest reqt = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession ses = reqt.getSession(false);
String reqURI = reqt.getRequestURI();
if (reqURI.indexOf("/login.xhtml") >= 0
|| (ses != null && ses.getAttribute("username") != null)
|| reqURI.indexOf("/public/") >= 0
|| reqURI.contains("javax.faces.resource"))
chain.doFilter(request, response);
else
resp.sendRedirect(reqt.getContextPath() + "/faces/login.xhtml");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
@Override
public void destroy() {
}
}
Ta đã triển khai interface lọc tiêu chuẩn bằng cách ghi đè các phương thức destroy và doFilter. Trong phương thức doFilter, ta sẽ chuyển hướng người dùng về trang đăng nhập nếu họ cố gắng truy cập một trang khác mà chưa đăng nhập.
Bước 9: Tạo file admin.xhtml như sau:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"<https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>">
<html xmlns="<https://www.w3.org/1999/xhtml>"
xmlns:h="<https://java.sun.com/jsf/html>">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<p>Welcome #{login.user}</p>
<h:commandLink action="#{login.logout}" value="Logout"></h:commandLink>
</h:form>
</h:body>
</html>
Trang này sẽ được hiển thị khi người dùng đăng nhập thành công. Chức năng đăng xuất được thực hiện bằng cách gọi phương thức logout của class Login.java.
Bước 10: Tạo file faces-config.xml như sau:
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2" xmlns="<https://xmlns.jcp.org/xml/ns/javaee>"
xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>"
xsi:schemaLocation="<https://xmlns.jcp.org/xml/ns/javaee>
<https://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd>">
<navigation-rule>
<from-view-id>/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>admin</from-outcome>
<to-view-id>/admin.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
Sau khi hoàn tất các bước trên, hãy chạy ứng dụng và bạn sẽ thấy các trang sau trong trình duyệt.
Trang Đăng nhập

Trang Lỗi Xác thực

Trang Đăng nhập Thành công

Truy cập admin.xhtml khi đã đăng nhập

Bạn chỉ cần nhấn vào liên kết Logout, session sẽ bị vô hiệu hóa. Sau đó, nếu thử truy cập lại trang admin.xhtml, bạn sẽ được chuyển hướng về trang đăng nhập.
Tổng kết
Như bạn đã thấy JSF cho phép ta dễ dàng xây dựng cơ chế xác thực người dùng với các bước cụ thể từ quản lý phiên đến kiểm tra thông tin đăng nhập dựa trên cơ sở dữ liệu, giúp bảo vệ trang web khỏi truy cập trái phép một cách hiệu quả. Hãy tải project ví dụ ở trên từ link này để trực tiếp thử nghiệm và biết cách ứng dụng vào chính project của bạn.