JSF דוגמת מסד נתונים להתחברות והתנתקות מאומתת

המנגנון אימות מאפשר למשתמשים לקבל גישה מאובטחת ליישום על ידי אימות שם המשתמש והסיסמה. נשתמש ב- JSF view להתחברות, אובייקט DAO, HttpSession לניהול הסשן, JSF managed bean ובמסד נתונים mysql. נבחן כעת בפרטים איך ליצור מנגנון אימות התחברות עם JSF ביישום JSF.

שלב 1: יצירת טבלה Users במסד הנתונים mysql כ

CREATE TABLE Users( 
uid int(20) NOT NULL AUTO_INCREMENT, 
uname VARCHAR(60) NOT NULL, 
password VARCHAR(60) NOT NULL, 
PRIMARY KEY(uid));

כאן אנו יוצרים טבלת משתמשים עם uid כמפתח ראשי, שדות שם משתמש וסיסמה עם אילוצים not null.

שלב 2: הכנסת נתונים לטבלה Users כ;

INSERT INTO Users VALUES(1,'adam','adam');

לפני שנמשיך לקוד הקשור לפרויקט שלנו, בתמונה למטה מוצגת מבנה הפרויקט ב- Eclipse. פשוט צרו פרויקט דינמי ברשת והמירו אותו למייבן כדי לקבל את תבנית הפרויקט ואז המשיכו להוסיף רכיבים שונים.

שלב 3: יצירת דף התחברות JSF login.xhtml כ;

<?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>
</html>

כאן אנו יוצרים דף תצוגת התחברות JSF עם שדות שם משתמש וסיסמה ומגדירים ערכים עבור שדות אלו דרך ה- managed bean של התחברות. אנו קוראים לשיטת validateUsernamePassword בלחיצה על לחצן ההתחברות כדי לאמת שם משתמש וסיסמה.

שלב 4: יצירת ה managed bean Login.java כ;

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;
	}

	// אימות התחברות
	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";
		}
	}

	// אירוע התנתקות, ביטול הסשן
	public String logout() {
		HttpSession session = SessionUtils.getSession();
		session.invalidate();
		return "login";
	}
}

אנו מצהירים על שלושה משתנים מחרוזת user, pwd ו-msg עבור שדות שם משתמש, סיסמה והודעת שגיאה יחד עם שיטות getter ו-setter. אנו כותבים שיטה validateUsernamePassword() לאימות שדה שם המשתמש והסיסמה על ידי קריאה למחלקת LoginDAO כדי לאחזר את שם המשתמש והסיסמה ממסד הנתונים ולהשוות אותם עם הערכים שנשלחו מהקדמה. אם שם המשתמש והסיסמה לא תואמים הודעת שגיאה מוצגת כ"שם משתמש או סיסמה שגויים". גם שיטת logout() כתובה כדי לבצע התנתקות על ידי ביטול HTTPSession המצורפת.
שלב 5: עכשיו ניצור את מחלקת ה-Java LoginDAO כך שלמטה. שים לב שקוד פעולות מסד הנתונים אינו מותאם לשימוש בפרויקט אמיתי, כתבתי אותו במהירות ככל האפשר מכיוון שהרעיון הוא ללמוד אימות ביישומי 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()) {
				// תוצאה נמצאה, משמע שהקלט תקין

				return true;
			}
		} catch (SQLException ex) {
			System.out.println("Login error -->" + ex.getMessage());
			return false;
		} finally {
			DataConnect.close(con);
		}
		return false;
	}
}

בשיטת validate() ראשית אנו בונים חיבור למסד הנתונים על ידי קריאה לשיטת getConnection של מחלקת DataConnect. אנו משתמשים ב-PreparedStatement כדי לבנות את השאילתה לאחזור הנתונים ממסד הנתונים עם הערכים שהוזנו על ידי המשתמש. אם קיבלנו נתונים בתוצאה מקבוצת התוצאות, זה אומר שהקלט תקין ואנו מחזירים אמת, אחרת שקר.
שלב 6: ניצור את מחלקת ה-Java DataConnect כך:

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) {
		}
	}
}

אנו טוענים את נהג ה-JDBC באמצעות השיטה Class.forName ומשתמשים בשיטת DriverManager.getConnection כדי להתחבר למסד הנתונים, על פי ה-URL, שם המשתמש והסיסמה. שלב 7: צור את SessionUtils.java כדי לקבל ולנהל מידע קשור למשתמש בקשר לסשן.

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;
	}
}

כאן אנו משיגים סשן עבור כל משתמש שנכנס דרך השיטה getUserId, ובכך אנו משייכים מזהה סשן למזהה משתמש מסויים. שלב 8: צור מחלקת מסנן האישור כך ש־

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() {

	}
}

נממש את מחלקת הסנן התקנית על ידי דרך כתיבת שיטות ה destroy ו־doFilter. בשיטת doFilter נעביר את המשתמש לדף ההתחברות אם ינסה לגשת לדף אחר בלעדי כניסה. שלב 9: צור את admin.xhtml כך ש־

<?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>

דף זה יוצג כאשר המשתמש נכנס בהצלחה. פונקציות ההתנתקות מיושמות על ידי קריאה לשיטת ההתנתקות של מחלקת Login.java. שלב 10: צור קובץ faces-config.xml כך ש־

<?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>

פעם שתסיים את כל השלבים שצוינו לעיל, הפעל את היישום וראה את הפלט הבא בדפדפן. דף התחברות דף שגיאת אימות דף התחברות הצליחה גישה ל-admin.xhtml כאשר מחובר פשוט לחץ על קישור ההתנתקות והסשן ייבוטל. לאחר מכן נסה לגשת לעמוד admin.xhtml ותופנה לדף ההתחברות. עבור והורד את הפרויקט מהקישור למטה ונסה אותו.

הורד את פרויקט ההתחברות והתנתקות ב-JSF

Source:
https://www.digitalocean.com/community/tutorials/jsf-authentication-login-logout-database-example