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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Servlet - 会话跟踪

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

Servlet

標(biāo)簽 : Java與Web


會(huì)話跟蹤

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

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

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


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

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

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

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


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

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.
  • 示例: 獲取上次訪問(wèn)時(shí)間
    Request中獲取Cookie: last_access_time, 如果沒(méi)有則新建,否則顯示last_access_time內(nèi)容, 并更新為當(dāng)前系統(tǒng)時(shí)間, 最后放入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)訪問(wèn)過(guò)了if (latCookie != null){printResponse("您上次訪問(wèn)的時(shí)間是" + latCookie.getValue(), response);latCookie.setValue(new Date().toString());} else{printResponse("您還是第一次訪問(wèn)", 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類通過(guò)getMaxAge()與setMaxAge(int maxAge)方法來(lái)讀寫Max-Age屬性:

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

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

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

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

如果沒(méi)有指定域, 則Domain默認(rèn)為產(chǎn)生Set-Cookie響應(yīng)的服務(wù)器主機(jī)名.


路徑屬性

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

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

這樣如果訪問(wèn)http://www.example.com/hello_http_servlet.do就不會(huì)獲得last_access_time,但如果訪問(wèn)http://www.example.com/servlet/index.html, 就會(huì)帶上這個(gè)Cookie.

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

如果沒(méi)有指定路徑, Path默認(rèn)為產(chǎn)生Set-Cookie響應(yīng)的URL的路徑.


Session

在所有的會(huì)話跟蹤技術(shù)中, Session是功能最強(qiáng)大,最多的. 每個(gè)用戶可以沒(méi)有或者有一個(gè)HttpSession對(duì)象, 并且只能訪問(wèn)他自己的Session對(duì)象.

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

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

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.

示例-購(gòu)物車

  • 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;}// ... }
  • 商品列表頁(yè)面(/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>
  • 加入購(gòu)物車(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);} }
  • 購(gòu)物車(/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有一定的過(guò)期時(shí)間: 當(dāng)用戶長(zhǎng)時(shí)間不去訪問(wèn)該Session,就會(huì)超時(shí)失效,雖然此時(shí)sessionID可能還在Cookie中, 只是服務(wù)器根據(jù)該sessionID已經(jīng)找不到Session對(duì)象了.
Session的超時(shí)時(shí)間可以在web.xml中配置, 單位為分鐘:

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

另外一種情況: 由于sessionID保存在Cookie中且Max-Age為-1,因此當(dāng)用戶重新打開(kāi)瀏覽器時(shí)已經(jīng)沒(méi)有sessionID了, 此時(shí)服務(wù)器會(huì)再創(chuàng)建一個(gè)Session,此時(shí)新的會(huì)話又開(kāi)始了.而原先的Session會(huì)因?yàn)槌瑫r(shí)時(shí)間到達(dá)而被銷毀.


字符編碼

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


響應(yīng)編碼

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

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

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

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

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

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

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


請(qǐng)求編碼

1. 瀏覽器地址欄編碼

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

瀏覽器編碼
IE/FireFoxGB2312
ChromeUTF-8

2. 頁(yè)面請(qǐng)求

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

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

3. GET

當(dāng)客戶端發(fā)送GET請(qǐng)求時(shí),無(wú)論客戶端發(fā)送的數(shù)據(jù)編碼方式為何,服務(wù)端均已ISO-8859-1解碼(Tomcat8.x之后改用UTF-8),這就需要我們?cè)趓equest.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

當(dāng)客戶端發(fā)送POST請(qǐng)求時(shí),服務(wù)端也是默認(rèn)使用IOS-8859-1解碼,但POST的數(shù)據(jù)是通過(guò)請(qǐng)求體傳送過(guò)來(lái),因此POST請(qǐng)求可以通過(guò)request.setCharacterEncoding()來(lái)指定請(qǐng)求體編碼方式:

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)絡(luò)標(biāo)準(zhǔn)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]、一些特殊符號(hào)"$-_.+!*'(),"[不包括雙引號(hào)]、以及某些保留字,才可以不經(jīng)過(guò)編碼直接用于URL。”

如果URL中有漢字,就必須編碼后使用, 而URL編碼過(guò)程其實(shí)很簡(jiǎn)單:

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

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

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容器會(huì)自動(dòng)識(shí)別URL是否已經(jīng)編碼并自動(dòng)解碼.


參考

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


總結(jié)

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

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