日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Servlet - 会话跟踪

發(fā)布時間:2025/3/17 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Servlet - 会话跟踪 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Servlet

標簽 : Java與Web


會話跟蹤

HTTP本身是“無狀態(tài)”協(xié)議,它不保存連接交互信息,一次響應完成之后即連接斷開,下一次請求需要重新建立連接,服務器不記錄上次連接的內(nèi)容.因此如果判斷兩次連接是否是同一用戶, 就需要使用會話跟蹤技術(shù)來解決.常見的會話跟蹤技術(shù)有如下幾種:

  • URL重寫: 在URL結(jié)尾附加會話ID標識,服務器通過會話ID識別不同用戶.
  • 隱藏表單域: 將會話ID埋入HTML表單隱藏域提交到服務端(會話ID不在瀏覽器頁面顯示).
  • Cookie: 第一次請求時服務器主動發(fā)一小段信息給瀏覽器(即Cookie),下次請求時瀏覽器自動附帶該段信息發(fā)送給服務器,服務器讀取Cookie識別用戶.
  • Session: 服務器為每個用戶創(chuàng)建一個Session對象保存到內(nèi)存,并生成一個sessionID放入Cookie發(fā)送給瀏覽器,下次訪問時sessionID會隨Cookie傳回來,服務器再根據(jù)sessionID找到對應Session對象(Java領(lǐng)域特有).

Session機制依賴于Cookie,如果Cookie被禁用Session也將失效.


Cookie是識別當前用戶,實現(xiàn)持久會話的最好方式.最初由網(wǎng)景公司開發(fā),但現(xiàn)在所有主流瀏覽器都支持.以至于HTTP協(xié)議為他定義了一些新的HTTP首部.

URL重寫與隱藏表單域兩種技術(shù)都有一定的局限,細節(jié)可參考博客四種會話跟蹤技術(shù)

  • Cookie規(guī)范
    • Cookie通過請求頭/響應頭在服務器與客戶端之間傳輸, 大小限制為4KB;
    • 一臺服務器在一個客戶端最多保存20個Cookie;
    • 一個瀏覽器最多保存300個Cookie;

Cookie的key/value均不能保存中文,如果需要,可以在保存前對中文進行編碼, 取出時再對其解碼.


在Java中使用Cookie, 必須熟悉javax.servlet.http.Cookie類, 以及HttpServletRequest/HttpServletResponse接口提供的幾個方法:

Cookie描述
Cookie(String name, String value)Constructs a cookie with the specified name and value.
String getName()Returns the name of the cookie.
String getValue()Gets the current value of this Cookie.
void setValue(String newValue)Assigns a new value to this Cookie.
void setMaxAge(int expiry)Sets the maximum age in seconds for this Cookie.
int getMaxAge()Gets the maximum age in seconds of this Cookie.
void setPath(String uri)Specifies a path for the cookie to which the client should return the cookie.
void setDomain(String domain)Specifies the domain within which this cookie should be presented.
Request描述
Cookie[] getCookies()Returns an array containing all of the Cookie objects the client sent with this request.
Response描述
void addCookie(Cookie cookie)Adds the specified cookie to the response.
  • 示例: 獲取上次訪問時間
    Request中獲取Cookie: last_access_time, 如果沒有則新建,否則顯示last_access_time內(nèi)容, 并更新為當前系統(tǒng)時間, 最后放入Response:
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Cookie[] cookies = request.getCookies();Cookie latCookie = null;if (cookies != null){for (Cookie cookie : cookies){if (cookie.getName().equals(L_A_T)){latCookie = cookie;break;}}}// 已經(jīng)訪問過了if (latCookie != null){printResponse("您上次訪問的時間是" + latCookie.getValue(), response);latCookie.setValue(new Date().toString());} else{printResponse("您還是第一次訪問", response);latCookie = new Cookie(L_A_T, new Date().toString());}response.addCookie(latCookie); }private void printResponse(String data, HttpServletResponse response) throws IOException {response.setContentType("text/html; charset=utf-8");response.getWriter().print("<H1>" + data + "</H1>"); }

有效期

Cookie的Max-Age決定了Cookie的有效期,單位為秒.Cookie類通過getMaxAge()與setMaxAge(int maxAge)方法來讀寫Max-Age屬性:

Max-Age描述
0Cookie立即作廢(如果原先瀏覽器已經(jīng)保存了該Cookie,那么可以通過設置Max-Age為0使其失效)
< 0默認,表示只在瀏覽器內(nèi)存中存活,一旦瀏覽器關(guān)閉則Cookie銷毀
> 0將Cookie持久化到硬盤上,有效期由Max-Age決定

Set-Cookie: last_access_time="xxx"; Domain=.fq.com

該響應首部就是在告訴瀏覽器將Cookie last_access_time="xxx"發(fā)送給域”.fq.com”中的所有站點(如www.fq.com, mail.fq.com).

Cookie類通過setDomain()方法設置域?qū)傩?

如果沒有指定域, 則Domain默認為產(chǎn)生Set-Cookie響應的服務器主機名.


路徑屬性

Cookie規(guī)范允許用戶將Cookie與部分Web站點關(guān)聯(lián)起來.該功能可通過向Set-Cookie響應首部添加Path屬性來實現(xiàn):

Set-Cookie:last_access_time="Tue Apr 26 19:35:16 CST 2016"; Path=/servlet/

這樣如果訪問http://www.example.com/hello_http_servlet.do就不會獲得last_access_time,但如果訪問http://www.example.com/servlet/index.html, 就會帶上這個Cookie.

Cookie類中通過setPath()方法設置路徑屬性.

如果沒有指定路徑, Path默認為產(chǎn)生Set-Cookie響應的URL的路徑.


Session

在所有的會話跟蹤技術(shù)中, Session是功能最強大,最多的. 每個用戶可以沒有或者有一個HttpSession對象, 并且只能訪問他自己的Session對象.

與URL重寫, 隱藏表單域和Cookie不同, Session是保存在服務器內(nèi)存中的數(shù)據(jù),在達到一定的閾值后, Servlet容器會將Session持久化到輔助存儲器中, 因此最好將使保存到Session內(nèi)的對象實現(xiàn)java.io.Serializable接口.

使用Session, 必須熟悉javax.servlet.http.HttpSession接口, 以及HttpServletRequest接口中提供的幾個方法:

HttpSession描述
void setAttribute(String name, Object value)Binds an object to this session, using the name specified.
Object getAttribute(String name)Returns the object bound with the specified name in this session, or null if no object is bound under the name.
void invalidate()Invalidates this session then unbinds any objects bound to it.
Enumeration<String> getAttributeNames()Returns an Enumeration of String objects containing the names of all the objects bound to this session.
void removeAttribute(String name)Removes the object bound with the specified name from this session.
String getId()Returns a string containing the unique identifier assigned to this session.
boolean isNew()Returns true if the client does not yet know about the session or if the client chooses not to join the session.
Request描述
HttpSession getSession()Returns the current session associated with this request, or if the request does not have a session, creates one.
HttpSession getSession(boolean create)Returns the current HttpSession associated with this request or, if there is no current session and create is true, returns a new session.
String getRequestedSessionId()Returns the session ID specified by the client.

示例-購物車

  • domain
/*** @author jifang.* @since 2016/5/1 20:14.*/ public class Product implements Serializable {private int id;private String name;private String description;private double price;public Product(int id, String name, String description, double price) {this.id = id;this.name = name;this.description = description;this.price = price;}// ... } public class ShoppingItem implements Serializable {private Product product;private int quantity;public ShoppingItem(Product product, int quantity) {this.product = product;this.quantity = quantity;}// ... }
  • 商品列表頁面(/jsp/products.jsp)
<%@ page import="com.fq.web.domain.Product" %> <%@ page import="com.fq.web.util.ProductContainer" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Products</title> </head> <body> <h2>Products</h2> <ul><%for (Product product : ProductContainer.products) {%><li><%=product.getName()%>($<%=product.getPrice()%>)(<a href="${pageContext.request.contextPath}/jsp/product_details.jsp?id=<%=product.getId()%>">Details</a>)</li><%}%> </ul> <a href="${pageContext.request.contextPath}/jsp/shopping_cart.jsp">Shopping Cart</a> </body> </html>
  • 商品詳情(/jsp/product_details.jsp)
<%@ page import="com.fq.web.domain.Product" %> <%@ page import="com.fq.web.util.ProductContainer" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Product Details</title> </head> <body> <h2>Product Details</h2> <%int id = Integer.parseInt(request.getParameter("id"));Product product = ProductContainer.getProduct(id);assert product != null; %> <form action="${pageContext.request.contextPath}/session/add_to_card.do" method="post"><input type="hidden" name="id" value="<%=id%>"/><table><tr><td>Name:</td><td><%=product.getName()%></td></tr><tr><td>Price:</td><td><%=product.getPrice()%></td></tr><tr><td>Description:</td><td><%=product.getDescription()%></td></tr><tr><td><input type="text" name="quantity"></td><td><input type="submit" value="Buy"></td></tr><tr><td><a href="${pageContext.request.contextPath}/jsp/products.jsp">Products</a></td><td><a href="${pageContext.request.contextPath}/jsp/shopping_cart.jsp">Shopping Cart</a></td></tr></table> </form> </body> </html>
  • 加入購物車(AddCardServlet)
@WebServlet(name = "AddCardServlet", urlPatterns = "/session/add_to_card.do") public class AddCardServlet extends HttpServlet {@SuppressWarnings("All")protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {int id = Integer.parseInt(request.getParameter("id"));Product product = ProductContainer.getProduct(id);int quantity = Integer.parseInt(request.getParameter("quantity"));HttpSession session = request.getSession();List<ShoppingItem> items = (List<ShoppingItem>) session.getAttribute(SessionConstant.CART_ATTRIBUTE);if (items == null) {items = new ArrayList<ShoppingItem>();session.setAttribute(SessionConstant.CART_ATTRIBUTE, items);}items.add(new ShoppingItem(product, quantity));request.getRequestDispatcher("/jsp/products.jsp").forward(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);} }
  • 購物車(/jsp/shopping_card.jsp)
<%@ page import="com.fq.web.constant.SessionConstant" %> <%@ page import="com.fq.web.domain.ShoppingItem" %> <%@ page import="java.util.List" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Shopping Cart</title> </head> <body> <h2>Shopping Cart</h2> <a href="${pageContext.request.contextPath}/jsp/products.jsp">Products</a> <table><tr><td style="width: 150px">Quantity</td><td style="width: 150px">Product</td><td style="width: 150px">Price</td><td>Amount</td></tr><%List<ShoppingItem> items = (List<ShoppingItem>) session.getAttribute(SessionConstant.CART_ATTRIBUTE);if (items != null) {double total = 0.0;for (ShoppingItem item : items) {double subtotal = item.getQuantity() * item.getProduct().getPrice();%><tr><td><%=item.getQuantity()%></td><td><%=item.getProduct().getName()%></td><td><%=item.getProduct().getPrice()%></td><td><%=subtotal%></td></tr><%total += subtotal;}%><tr><td>Total: <%=total%></td></tr><%}%></table> </body> </html>

有效期

Session有一定的過期時間: 當用戶長時間不去訪問該Session,就會超時失效,雖然此時sessionID可能還在Cookie中, 只是服務器根據(jù)該sessionID已經(jīng)找不到Session對象了.
Session的超時時間可以在web.xml中配置, 單位為分鐘:

<session-config><session-timeout>30</session-timeout> </session-config>

另外一種情況: 由于sessionID保存在Cookie中且Max-Age為-1,因此當用戶重新打開瀏覽器時已經(jīng)沒有sessionID了, 此時服務器會再創(chuàng)建一個Session,此時新的會話又開始了.而原先的Session會因為超時時間到達而被銷毀.


字符編碼

字符編碼就是以二進制的數(shù)字來對應字符集的字符,常見字符編碼方式有:ISO-8859-1(不支持中文),GB2312,GBK,UTF-8等.在JavaWeb中, 經(jīng)常遇到的需要編碼/解碼的場景有響應編碼/請求編碼/URL編碼:


響應編碼

服務器發(fā)送數(shù)據(jù)給客戶端由Response對象完成,如果響應數(shù)據(jù)是二進制流,就無需考慮編碼問題.如果響應數(shù)據(jù)為字符流,那么就一定要考慮編碼問題:

response.getWriter()默認使用ISO-889-1發(fā)送數(shù)據(jù),而該字符集不支持中文,因此遇到中文就一定會亂碼.

在需要發(fā)送中文時, 需要使用:

response.setCharacterEncoding("UTF-8"); // getWriter() ...

設置編碼方式,由于在getWriter()輸出前已經(jīng)設置了UTF-8編碼,因此輸出字符均為UTF-8編碼,但我們并未告訴客戶端使用什么編碼來讀取響應數(shù)據(jù),因此我們需要在響應頭中設置編碼信息(使用Content-Type):

response.setContentType("text/html;charset=UTF-8"); // getWriter() ...

注意: 這句代碼不只在響應頭中添加了編碼信息,還相當于調(diào)用了一次response.setCharacterEncoding("UTF-8");


請求編碼

1. 瀏覽器地址欄編碼

在瀏覽器地址欄書寫字符數(shù)據(jù),由瀏覽器編碼后發(fā)送給服務器,因此如果在地址欄輸入中文,則其編碼方式由瀏覽器決定:

瀏覽器編碼
IE/FireFoxGB2312
ChromeUTF-8

2. 頁面請求

如果通過頁面的超鏈接/表單向服務器發(fā)送數(shù)據(jù),那么其編碼方式由當前頁面的編碼方式確定:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

3. GET

當客戶端發(fā)送GET請求時,無論客戶端發(fā)送的數(shù)據(jù)編碼方式為何,服務端均已ISO-8859-1解碼(Tomcat8.x之后改用UTF-8),這就需要我們在request.getParameter()獲取數(shù)據(jù)后再轉(zhuǎn)換成正確的編碼:

private Map<String, String> convertToParameterMap(HttpServletRequest request) throws UnsupportedEncodingException {Enumeration<String> names = request.getParameterNames();Map<String, String> parameters = new HashMap<String, String>();if (names != null) {while (names.hasMoreElements()) {String name = names.nextElement();String value = request.getParameter(name);parameters.put(name, new String(value.getBytes("ISO-8859-1"), "UTF-8"));}}return parameters; }

4. POST

當客戶端發(fā)送POST請求時,服務端也是默認使用IOS-8859-1解碼,但POST的數(shù)據(jù)是通過請求體傳送過來,因此POST請求可以通過request.setCharacterEncoding()來指定請求體編碼方式:

private Map<String, String> convertToParameterMap(HttpServletRequest request) throws IOException {Map<String, String> parameters = new HashMap<String, String>();if (request.getMethod().equals("POST")) {request.setCharacterEncoding("UTF-8");Enumeration<String> names = request.getParameterNames();while (names.hasMoreElements()) {String key = names.nextElement();parameters.put(key, request.getParameter(key));}} else {Enumeration<String> names = request.getParameterNames();while (names.hasMoreElements()) {String key = names.nextElement();String value = request.getParameter(key);parameters.put(key, new String(value.getBytes("ISO-8859-1"), "UTF-8"));}}return parameters; }

URL編碼

網(wǎng)絡標準RFC 1738規(guī)定:

“…Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL.”
“只有字母和數(shù)字[0-9a-zA-Z]、一些特殊符號"$-_.+!*'(),"[不包括雙引號]、以及某些保留字,才可以不經(jīng)過編碼直接用于URL。”

如果URL中有漢字,就必須編碼后使用, 而URL編碼過程其實很簡單:

首先需要指定一種字符編碼,把字符串解碼后得到byte[],然后把小于0的字節(jié)+256,再將其轉(zhuǎn)換成16進制,最后前面再添加一個%.

這個編碼過程在Java中已經(jīng)封裝成了現(xiàn)成的庫, 可直接使用:

URLEncoder描述
static String encode(String s, String enc)Translates a string into application/x-www-form-urlencoded format using a specific encoding scheme.
URLDecoder描述
static String decode(String s, String enc)Decodes a application/x-www-form-urlencoded string using a specific encoding scheme.

注: 在Web中Tomcat容器會自動識別URL是否已經(jīng)編碼并自動解碼.


參考

更多有關(guān)編碼知識, 可以參考:
1. 阮一峰: 關(guān)于URL編碼
2. Web開發(fā)者應知的URL編碼知識
3. 字符集和字符編碼(Charset & Encoding)


總結(jié)

以上是生活随笔為你收集整理的Servlet - 会话跟踪的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。