ניהול סשן ב-Java אפליקציות אינטרנט באמצעות Servlet הוא נושא מעניין מאוד. סשן ב-Java Servlet ניהול מתבצע באמצעות שיטות שונות, כמו עוגיות, API HttpSession, כתיבת URL וכו '. זהו המאמר השלישי בסדרת המדריכים של אפליקציות האינטרנט ב-Java, כדאי לבדוק גם את שני המאמרים הקודמים.
ניהול סשן ב-Java
מטרת המאמר היא להסביר על ניהול הסשן ב-Servlet באמצעות טכניקות שונות עם דוגמאות.
-
מהו השיח?
פרוטוקול HTTP ושרתי האינטרנט הם בלתי מתמידים, מה שאומר הוא כי לכל בקשה שרת האינטרנט היא בקשה חדשה לעיבוד והם לא יכולים לזהות אם היא באה מלקוח ששלח בקשה מראש. אך לעיתים קרובות ביישומי האינטרנט, עלינו לדעת מי הלקוח ולעבד את הבקשה בהתאם. לדוגמה, יישום עגלת הקניות צריך לדעת מי שולח את הבקשה להוספת פריט ובאיזו עגלה יש להוסיף את הפריט או מי שולח בקשת הקנייה כדי שנוכל לחייב את הסכום ללקוח הנכון. השיח הוא מצב שיחה בין הלקוח והשרת והוא יכול להכיל מספר בקשות ותגובות בין הלקוח והשרת. מאחר ופרוטוקול HTTP ושרת האינטרנט הם שניים לא מתמידים, הדרך היחידה לשמור על השיח היא כאשר מידע ייחודי על השיח (מזהה השיח) עובר בין השרת והלקוח בכל בקשה ותגובה. ישנן מספר דרכים שבאמצעותן אנו יכולים לספק מזהה ייחודי בבקשה ובתגובה.
-
אימות משתמש – זוהי הדרך הנפוצה ביותר, כאשר המשתמש יכול לספק פרטי אימות מהעמוד כניסה ואז אנו יכולים להעביר את מידע האימות בין השרת והלקוח כדי לשמור על השיח. זו אינה שיטה מאוד אפקטיבית מכיוון שהיא לא תעבוד אם אותו משתמש מחובר מדפדפנים שונים.
-
שדה נסתר של HTML – נוכל ליצור שדה נסתר ייחודי ב-HTML וכאשר המשתמש מתחיל לנווט, נוכל להגדיר את ערכו באופן ייחודי למשתמש ולעקוב אחר השיח. לא ניתן להשתמש בשיטה זו עם קישורים מאחר ונדרש לשלוח את הטופס בכל פעם שבקשה מועברת מלקוח לשרת עם השדה הנסתר. בנוסף, זו אינה שיטה מאובטחת מכיוון שניתן לקבל את ערך השדה הנסתר ממקור ה-HTML ולהשתמש בו כדי לפרוץ לשיח.
-
כתיבת URL מחדש – נוכל להוסיף פרמטר מזהה של השיח עם כל בקשה ותגובה כדי לעקוב אחר השיח. זה מאוד מפריע מאחר ויש לשמור על פרמטר זה בכל תגובה ולוודא שהוא לא פוגע בפרמטרים אחרים.
-
ניהול סשן ב-Java – עוגיות
עוגיות משמשות הרבה ביישומי אינטרנט כדי להתאים אישית תגובות בהתאם לבחירה שלך או לעקוב אחר הסשן. לפני שנמשיך אל ניהול הסשן ב-Servlet, אשמח להראות כיצד נוכל לעקוב אחר הסשן עם עוגיות דרך יישום אינטרנט קטן. ניצור יישום אינטרנט דינמי ServletCookieExample עם מבנה הפרויקט כמו בתמונה למטה.
מחבר ההפעלה web.xml של היישום האינטרנטי הוא:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>ServletCookieExample</display-name> <welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list> </web-app>
דף הבית של היישום שלנו הוא login.html שבו נקבל פרטי אימות מהמשתמש.
<!DOCTYPE html> <html> <head> <meta charset="US-ASCII"> <title>דף התחברות</title> </head> <body> <form action="LoginServlet" method="post"> שם משתמש: <input type="text" name="user"> <br> סיסמה: <input type="password" name="pwd"> <br> <input type="submit" value="התחברות"> </form> </body> </html>
הנה ה־LoginServlet שמטפל בבקשת ההתחברות.
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "Pankaj"; private final String password = "journaldev"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // get request parameters for userID and password String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ Cookie loginCookie = new Cookie("user",user); //setting cookie to expiry in 30 mins loginCookie.setMaxAge(30*60); response.addCookie(loginCookie); response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>השם משתמש או הסיסמה שגויים.</font>"); rd.include(request, response); } } }
שימו לב לעוגיה שאנו מגדירים ואז מעבירים אותה לתגובה ואז לדף LoginSuccess.jsp, עוגיה זו תשמש שם לעקוב אחר הסשן. כן שימו לב כי זמן התפוגה של העוגיה מוגדר ל-30 דקות. באידיאל עלינו להשתמש בלוגיקה מורכבת יותר כדי להגדיר את ערך העוגיה למעקב אחר הסשן כך שלא יתקולל עם בקשה אחרת כלשהי.
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>דף הצלחת התחברות</title>
</head>
<body>
<%
String userName = null;
Cookie -
הפעלה ב-Java Servlet – HttpSession
ממשק ה-HttpSession מספק ניהול סשן דרך API של סרבל. אנו יכולים לקבל סשן מאובייקט HttpServletRequest באמצעות השיטות הבאות. HttpSession מאפשר לנו להגדיר אובייקטים כמאפיינים שניתן לאחזר בבקשות עתידיות.
- HttpSession getSession() – שיטה זו מחזירה תמיד אובייקט HttpSession. היא מחזירה את אובייקט הסשן המצורף לבקשה, אם אין סשן מצורף לבקשה, אז היא יוצרת סשן חדש ומחזירה אותו.
- HttpSession getSession(boolean flag) – שיטה זו מחזירה אובייקט HttpSession אם יש סשן בבקשה אחרת היא מחזירה null.
חלק מהשיטות החשובות של HttpSession הם:
- String getId() – מחזיר מחרוזת המכילה את הזיהוי היחודי המשויך לסשן זה.
- Object getAttribute(String name) – מחזיר את האובייקט הקשור בשם המסוים בסשן זה, או null אם אין אובייקט שקשור תחת השם. כמה שיטות אחרות לעבוד עם מאפייני הסשן הם
getAttributeNames()
,removeAttribute(String name)
ו־setAttribute(String name, Object value)
. - long getCreationTime() – מחזיר את הזמן בו נוצר הסשן הזה, מומד במילישניות מאז חצות ב־1 בינואר 1970 GMT. אפשר לקבל את זמן הגישה האחרונה עם השיטה
getLastAccessedTime()
. - setMaxInactiveInterval(int interval) – מגדיר את הזמן, בשניות, בין בקשות הלקוח לפני שמערכת הסרבל תפסיק את הסשן הזה. אפשר לקבל את ערך הפסקת הסשן מהשיטה
getMaxInactiveInterval()
. - ServletContext getServletContext() – מחזיר אובייקט ServletContext עבור האפליקציה.
- boolean isNew() – מחזיר true אם הלקוח עדיין לא מכיר את הסשן או אם הלקוח בוחר לא להצטרף לסשן.
- void invalidate() – מבטל את הסשן הזה ומתנתק כל אובייקטים המקושרים אליו.
הבנת עוגיית JSESSIONID
כאשר אנו משתמשים בשיטת HttpServletRequest getSession() והיא יוצרת בקשה חדשה, היא יוצרת גם אובייקט HttpSession חדש ומוסיפה עוגייה לאובייקט תגובה בשם JSESSIONID וערך כזה של זיהוי סשן. העוגייה הזו משמשת לזיהוי של אובייקט HttpSession בבקשות נוספות מהלקוח. אם העוגיות מושבתות בצד הלקוח ואנו משתמשים ב-URL ריסון, אז השיטה הזו מ
-
ניהול סשן ב-Java Servlet – שינוי כתובת URL
כפי שראינו בחלק הקודם, אנו יכולים לנהל סשן באמצעות HttpSession אך אם נבטל את העוגיות בדפדפן, זה לא יעבוד מכיוון שהשרת לא יקבל את עוגיית JSESSIONID מהלקוח. חבילת ה- Servlet API מספקת תמיכה בשינוי כתובת URL שאנו יכולים להשתמש בה כדי לנהל סשן במקרה זה. החלק הכי טוב הוא שמנקודת מבט הקידוד, זה מאוד קל לשימוש ומעורב בשלב אחד בלבד – קידוד ה-URL. דבר טוב נוסף עם הקידוד של Servlet URL הוא שזו גישת גיבוי והיא נכנסת לתוקף רק אם העוגיות של הדפדפן מנוטרלות. אנו יכולים לקודד את ה-URL עם השיטה HttpServletResponse
encodeURL()
ואם עלינו להפנות את הבקשה למשאב אחר ונרצה לספק מידע סשן, אנו יכולים להשתמש בשיטהencodeRedirectURL()
. ניצור פרויקט דומה למעלה רק שנשתמש בשיטות של שינוי כתובת URL כדי לוודא שניהול הסשן עובד טוב גם כאשר העוגיות מנוטרלות בדפדפן. מבנה הפרויקט ServletSessionURLRewriting באיקליפס נראה כמו בתמונה למטה.חבילה com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * יישום של Servlet להתחברות */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "admin"; private final String password = "password"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // לקבל פרמטרי בקשה עבור מזהה המשתמש והסיסמה String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ HttpSession session = request.getSession(); session.setAttribute("user", "Pankaj"); // הגדרת סשן לפקיעה ב-30 דקות session.setMaxInactiveInterval(30*60); Cookie userName = new Cookie("user", user); response.addCookie(userName); //לקבל את מחרוזת ה-URL הקידודית String encodedURL = response.encodeRedirectURL("LoginSuccess.jsp"); response.sendRedirect(encodedURL); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Either user name or password is wrong.</font>"); rd.include(request, response); } } }
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>עמוד הצלחת התחברות</title>
</head>
<body>
<%
//לאפשר גישה רק אם יש סשן
String user = null;
if(session.getAttribute("user") == null){
response.sendRedirect("login.html");
}else user = (String) session.getAttribute("user");
String userName = null;
String sessionID = null;
Cookie[] cookies = request.getCookies();
if(cookies !=null){
for(Cookie cookie : cookies){
if(cookie.getName().equals("user")) userName = cookie.getValue();
if(cookie.getName().equals("JSESSIONID")) sessionID = cookie.getValue();
}
}else{
sessionID = session.getId();
}
%>
<h3>היי <%=userName %>, התחברות מוצלחת. מספר הזיהוי של הסשן שלך ה
זהו כל הנוגע לניהול סשן ב- Java Servlets, נבדוק בעתיד סרטונים על פילטרים של Servlet ועל מאזינים ועל עוגיות במאמרים עתידיים. עדכון: תבדוק את המאמר הבא בסדרהServlet Filter.