失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 会话管理:Session与Cookie

会话管理:Session与Cookie

时间:2019-05-23 21:07:38

相关推荐

会话管理:Session与Cookie

1. 导言

HTTP 是一种无状态协议,每次客户端访问Web页面时,客户端打开一个单独的浏览器窗口连接到Web服务器,由于服务器不会自动保存之前客户端请求的相关信息,所以无法识别一个HTTP请求是否为第一次访问。这意味着需要有相应的技术来维持Web客户端和服务器之间的会话,这就是会话跟踪。

2. Cookie与Session简单架构图

每一个客户端向服务器请求信息,服务器为每个客户端建立一个对话session,每个对话有一个唯一的sessionID,服务器将sessionID打包到cookie中,服务器将cookie发送给客户端。客户端保存cookie,等到下一次客户端请求服务器时,发送保存的cookie,服务器通过cookie里面的sessionID就能判别是哪一个客户端进行再次请求。

3.Cookies

3.1 Cookies简介

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。识别返回用户包括三个步骤:

(1)服务器脚本向浏览器发送一组 Cookie;

(2)浏览器将这些信息存储在本地计算机上,以备将来使用;

(3)当下一次浏览器向Web服务器发送任何请求时,浏览器会把这些Cookie信息发送到服务器,服务器将使用这些信息来识别用户。

3.2 Servlet中操作Cookie

Cookie源代码:

3.3 Cookie API

Cookie源代码:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package javax.servlet.http;import java.io.Serializable;import java.security.AccessController;import java.security.PrivilegedAction;import java.util.Locale;public class Cookie implements Cloneable, Serializable {private static final CookieNameValidator validation;private static final long serialVersionUID = 1L;private final String name;private String value;private int version = 0;private String comment;private String domain;private int maxAge = -1;private String path;private boolean secure;private boolean httpOnly;public Cookie(String name, String value) {validation.validate(name);this.name = name;this.value = value;}public void setComment(String purpose) {ment = purpose;}public String getComment() {return ment;}public void setDomain(String pattern) {this.domain = pattern.toLowerCase(Locale.ENGLISH);}public String getDomain() {return this.domain;}public void setMaxAge(int expiry) {this.maxAge = expiry;}public int getMaxAge() {return this.maxAge;}public void setPath(String uri) {this.path = uri;}public String getPath() {return this.path;}public void setSecure(boolean flag) {this.secure = flag;}public boolean getSecure() {return this.secure;}public String getName() {return this.name;}public void setValue(String newValue) {this.value = newValue;}public String getValue() {return this.value;}public int getVersion() {return this.version;}public void setVersion(int v) {this.version = v;}public Object clone() {try {return super.clone();} catch (CloneNotSupportedException var2) {throw new RuntimeException(var2);}}public void setHttpOnly(boolean httpOnly) {this.httpOnly = httpOnly;}public boolean isHttpOnly() {return this.httpOnly;}static {boolean strictServletCompliance;String propStrictNaming;String propFwdSlashIsSeparator;if (System.getSecurityManager() == null) {strictServletCompliance = Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");propStrictNaming = System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");propFwdSlashIsSeparator = System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");} else {strictServletCompliance = (Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {public Boolean run() {return Boolean.valueOf(System.getProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE"));}});propStrictNaming = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");}});propFwdSlashIsSeparator = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");}});}boolean strictNaming;if (propStrictNaming == null) {strictNaming = strictServletCompliance;} else {strictNaming = Boolean.parseBoolean(propStrictNaming);}boolean allowSlash;if (propFwdSlashIsSeparator == null) {allowSlash = !strictServletCompliance;} else {allowSlash = !Boolean.parseBoolean(propFwdSlashIsSeparator);}if (strictNaming) {validation = new RFC2109Validator(allowSlash);} else {validation = new RFC6265Validator();}}}

3.4 简单案例

setCookie.html

<!DOCTYPE html><html><head><meta charset="utf-8"><title>菜鸟教程()</title></head><body><form action="/setCookie" method="GET">站点名 :<input type="text" name="name"><br />站点 URL:<input type="text" name="url" /><br><input type="submit" value="提交" /></form></body></html>

package webstudy;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 java.io.IOException;import java.io.PrintWriter;import .URLEncoder;/*** Servlet implementation class HelloServlet*/@WebServlet("/SetCookie")public class SetCookie extends HttpServlet {private static final long serialVersionUID = 1L;/*** @see HttpServlet#HttpServlet()*/public SetCookie() {super();// TODO Auto-generated constructor stub}/*** @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)*/@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{// 为名字和姓氏创建 CookieCookie name = new Cookie("name",URLEncoder.encode(request.getParameter("name"), "UTF-8")); // 中文转码Cookie url = new Cookie("url",request.getParameter("url"));// 为两个 Cookie 设置过期日期为 24 小时后name.setMaxAge(60*60*24);url.setMaxAge(60*60*24);// 在响应头中添加两个 Cookieresponse.addCookie(name);response.addCookie(url);// 设置响应内容类型response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();String title = "设置 Cookie 实例";String docType = "<!DOCTYPE html>\n";out.println(docType +"<html>\n" +"<head><title>" + title + "</title></head>\n" +"<body bgcolor=\"#f0f0f0\">\n" +"<h1 align=\"center\">" + title + "</h1>\n" +"<ul>\n" +" <li><b>站点名:</b>:"+ request.getParameter("name") + "\n</li>" +" <li><b>站点 URL:</b>:"+ request.getParameter("url") + "\n</li>" +"</ul>\n" +"</body></html>");}/*** @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)*/@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}}

package webstudy;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 java.io.IOException;import java.io.PrintWriter;import .URLDecoder;import java.util.Arrays;/*** Servlet implementation class ReadCookies*/@WebServlet("/ReadCookies")public class ReadCookies extends HttpServlet {private static final long serialVersionUID = 1L;/*** @see HttpServlet#HttpServlet()*/public ReadCookies() {super();// TODO Auto-generated constructor stub}/*** @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)*/@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{Cookie cookie = null;Cookie[] cookies = null;// 获取与该域相关的 Cookie 的数组cookies = request.getCookies();System.out.println(Arrays.toString(cookies));// 设置响应内容类型response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();String title = "Delete Cookie Example";String docType = "<!DOCTYPE html>\n";out.println(docType +"<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" );if( cookies != null ){out.println("<h2>Cookie 名称和值</h2>");for (int i = 0; i < cookies.length; i++){cookie = cookies[i];//比较字符串的值是否相等,-1 0 1if((cookie.getName( )).compareTo("name") == 0 ){cookie.setMaxAge(0);//设置过期response.addCookie(cookie);//添加Cookieout.print("已删除的 cookie:" + cookie.getName( ) + "<br/>");}out.print("名称:" + cookie.getName( ) + ",");out.print("值:" + URLDecoder.decode(cookie.getValue(), "utf-8") +" <br/>");}}else{out.println( "<h2 class=\"tutheader\">No Cookie founds</h2>");}out.println("</body>");out.println("</html>");}/*** @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)*/@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}}

4. HttpSession 对象

4.1 Session 简介

为了让 Web 应用程序能够记住用户送出的不同请求,Servlet 规范内定义一个 HttpSession 接口,允许 Servlet 容器针对每个用户建立一个 HTTP 会话(即 HttpSession 对象),每个 HTTP 会话将被赋予惟一的“会话编号”(Session ID)。

HttpSession 对象会在用户第一次访问服务器时由容器创建(只有访问 JSP、Servlet 等动态资源时才会创建,访问 HTML 等静态资源并不会创建),当用户调用其失效方法(invalidate() 方法)或超过其最大不活动(超时,timeout )时间时会失效。

Servlet 容器为每个 HttpSession 生成唯一的标识,并将该标识发送给浏览器,或创建一个名为 JSESSIONID 的 cookie ,或者在 URL 后附加一个名为 jsessionid 的参数。在后续的请求中,浏览器会将标识提交给服务端,这样服务器就可以识别该请求是由哪个用户发起的。

4.2 HttpSession API

HttpSession 源代码:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package javax.servlet.http;import java.util.Enumeration;import javax.servlet.ServletContext;public interface HttpSession {long getCreationTime();String getId();long getLastAccessedTime();ServletContext getServletContext();void setMaxInactiveInterval(int var1);int getMaxInactiveInterval();/** @deprecated */@DeprecatedHttpSessionContext getSessionContext();Object getAttribute(String var1);/** @deprecated */@DeprecatedObject getValue(String var1);Enumeration<String> getAttributeNames();/** @deprecated */@DeprecatedString[] getValueNames();void setAttribute(String var1, Object var2);/** @deprecated */@Deprecatedvoid putValue(String var1, Object var2);void removeAttribute(String var1);/** @deprecated */@Deprecatedvoid removeValue(String var1);void invalidate();boolean isNew();}

4.3 简单案例

bookList.jsp页面显示图书购买列表,将表单数据提交给ProcessOneServlet.java处理,ProcessOneServlet.java将欲购买的图书数据放置在HttpSession中,并请求重定向到address.jsp页面,在address.jsp页面输入客户的用户名和邮寄地址,并提交给ProcessTwoServlet.java处理,ProcessTwoServlet.java将客户信息和邮寄地址数据放置在HttpSession中,并将请求重定向到ConfirmServlet.java,ConfirmServlet将购物信息从HttpSession中取出,进行显示,实现加入购物车的功能。

bookList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>选择购买书籍</title></head><body><h2>选择购买书籍</h2><form action="/ProcessOne" method="post"><table border="1" width="68%"><tr><th align="center">书名</td><th align="center">购买</td></tr><tr><td>Android Application Develoment Practice Tutorial</td><td align="center"><input type="checkbox" name="buy"value="Android Application Develoment Practice Tutorial"></td></tr><tr><td>Software Project Management</td><td align="center"><input type="checkbox" name="buy"value="Software Project Management"></td></tr><tr><td>Agile Software Development</td><td align="center"><input type="checkbox" name="buy"value="Agile Software Development"></td></tr><tr><td>Information Technology Project Management</td><td align="center"><input type="checkbox" name="buy"value="Information Technology Project Management"></td></tr></table><p><input type="submit" value="下一步"></p></form></body></html>

package book.ch4;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.io.PrintWriter;@WebServlet(name = "ProcessOneServlet", urlPatterns = {"/ProcessOne" })public class ProcessOneServlet extends HttpServlet {private static final long serialVersionUID = 1L;public ProcessOneServlet() {super();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();HttpSession session = request.getSession(true);String[] selectedBooks = request.getParameterValues("buy");session.setAttribute("choosed", selectedBooks);response.sendRedirect(request.getContextPath() + "/page/ch4/address.jsp");}}

address.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>邮寄地址</title></head><body><h2>输入邮寄地址</h2><form action="/ProcessTwo" method="post"><table border="1" width="68%"><tr><td>姓名:</td><td><input type="text" name="customer" size="30"></td></tr><tr><td>邮寄地址:</td><td><input type="text" name="address" size="66"></td></tr></table><p><input type="submit" value="下一步"></p></form></body></html>

package book.ch4;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.io.PrintWriter;@WebServlet(name = "ProcessTwoServlet", urlPatterns = {"/ProcessTwo" })public class ProcessTwoServlet extends HttpServlet {private static final long serialVersionUID = 1L;public ProcessTwoServlet() {super();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();HttpSession session = request.getSession(true);//String customer = request.getParameter("customer");String customer = new String(request.getParameter("customer").getBytes("iso-8859-1"), "utf-8");//String address = request.getParameter("address");String address = new String(request.getParameter("address").getBytes("iso-8859-1"), "utf-8");session.setAttribute("A_customer", customer);session.setAttribute("A_address", address);response.sendRedirect("Confirm");}}

package book.ch4;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.io.PrintWriter;@WebServlet(name = "/ConfirmServlet", urlPatterns = {"/Confirm" })public class ConfirmServlet extends HttpServlet {private static final long serialVersionUID = 1L;public ConfirmServlet() {super();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();HttpSession session = request.getSession(true);String[] books = (String[]) session.getAttribute("choosed");String customer = (String) session.getAttribute("A_customer");String address = (String) session.getAttribute("A_address");boolean conditon = (books != null) && (customer != null) && (address != null) && (!customer.equals("")) && (!address.equals(""));if (conditon) {System.out.println(conditon);System.out.println(!address.equals(""));System.out.println("customer=" + customer);System.out.println("address=" + address);out.println("<html><body>");out.println("<h2>订单确认</h2>");out.println("<table border=\"1\" width=\"68%\">");out.println("<tr>");out.println("<td>顾客姓名:</td>");out.println("<td>" + customer + "</td>");out.println("</tr>");out.println("<tr>");out.println("<td>地址:</td>");out.println("<td>" + address + "</td>");out.println("</tr>");out.println("<tr>");out.println("<td>订购书籍:</td>");out.println("<td>");for (int i = 0; i < books.length; i++) {out.println(books[i] + "<br>");}out.println("</td>");out.println("</tr>");out.println("</table>");out.println("<p><input type=\"submit\" value=\"提交订单\"></p>");out.println("</html></body>");} else {out.println("<html><body>");out.println("<h2>无法从HTTP会话内取出所需信息!</h2>");out.println("</html></body>");}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}}

5. URL 重写

在某些情况下,用户为了安全性可能会关闭 Web 浏览器的 Cookie 功能。这时候如果从用户端浏览器送出 HTTP 请求,并不会包含 Session ID,因此 Servlet 容器无法利用同一个 HTTP 会话来保存不同的 HTTP 请求内容。我们可以用URL重写解决这种情况。

5.1 什么是URL重写

在URL重写中,我们将标记或标识符添加到下一个Servlet或下一个资源的URL。我们可以使用以下格式发送参数名称/值对:

url?name1=value1&name2=value2

名称和值使用等号分隔,参数名称/值对与另一个参数使用&符号分隔。当用户单击超链接时,参数名称/值对将被传递到服务器。在Servlet中,我们可以使用getParameter()方法获取参数值。

5.2 URL重写的优势

无论是否禁用Cookie(与浏览器无关),它将始终有效。不需要在每个页面上提交额外的表单。

5.3 URL重写的缺点

它仅适用于URL链接。它只能发送文本(字符串)信息。

5.4 URL重写的案例

在下面的示例中,我们使用URL链接维护用户的状态。为了实现此目的,我们将用户名附加在查询字符串(queryString)中,并从另一页面的查询字符串(queryString)中获取值。

URL 重写通过HttpServletResponseencodeURL()方法和encodeRedirectURL()方法实现,其中 encodeRedirectURL() 方法主要对使用 sendRedirect () 方法的URL进行重写。

5.4.1 编写页面

UrlRewrite.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>URL重写案例</title></head><body><form action="/servlet1">用户名:<input type="text" name="userName"/><br/><input type="submit" value="提交"/></form></body></html>

5.4.2 编写FirstServlet

FirstServlet:

import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class FirstServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {try{response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();String n=request.getParameter("userName");out.print("欢迎 "+n);//把用户名追加到查询字符串后面out.print("<a href='"+request.getContextPath()+"/servlet2?uname="+n+"'>访问</a>");out.close();}catch(Exception e){System.out.println(e);}}}

5.4.3 编写SecondServlet

SecondServlet:

import javax.servlet.ServletException;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class SecondServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {try{response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();//从查询字符串获取用户名String n=request.getParameter("uname");out.print("你好 "+n);out.close();}catch(Exception e){System.out.println(e);}}}

5.4.4 配置web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="/xml/ns/javaee"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/xml/ns/javaee /xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>FirstServlet</servlet-name><servlet-class>book.ch4.FirstServlet</servlet-class></servlet><servlet-mapping><servlet-name>FirstServlet</servlet-name><url-pattern>/servlet1</url-pattern></servlet-mapping><servlet><servlet-name>SecondServlet</servlet-name><servlet-class>book.ch4.SecondServlet</servlet-class></servlet><servlet-mapping><servlet-name>SecondServlet</servlet-name><url-pattern>/servlet2</url-pattern></servlet-mapping></web-app>

5.5 隐藏表单域

一个 Web 服务器可以发送一个隐藏的 HTML 表单字段,以及一个唯一的 SESSION 会话 ID,如下所示:

<input type="hidden" name="session_id" value="123456789">

当表单被提交时,指定的名称和值会被自动包含在表单数据中。每次当用户端浏览器发送回请求时,session_id 值可以用于保持不同的用户端浏览器的跟踪。

6. 小结

Cookie 分为会话 Cookie持久 Cookie

会话 Cookie是指在不设定它的生命周期 expires 时的状态。

浏览器的开启到关闭就是一次会话,当关闭浏览器时,会话Cookie 就会跟随浏览器而销毁。

当关闭一个页面时,不影响会话 Cookie 的销毁。

持久 Cookie则是设定了它的生命周期 expires,关闭浏览器之后,它不会销毁,直到设定的过期时间。

对于持久 Cookie,可以在同一个浏览器中传递数据,比如,你在打开一个淘宝页面登陆后,你在点开一个商品页面,依然是登录状态,即便你关闭了浏览器,再次开启浏览器,依然会是登录状态。这就是因为 Cookie 自动将数据传送到服务器端,在反馈回来的结果。

Session 是通过 Cookie 技术实现的,依赖于名为 JSESSIONID 的 Cookie,它将信息保存在服务器端。

Session 中能够存储复杂的 Java 对象。

Session 机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

由于 Cookie 可以被人为的禁止,必须有其他机制以便在 Cookie 被禁止时仍然能够把 Session ID 传递回服务器。

于是我们可以采用URL 重写技术,就是把Session ID 直接附加在 URL 路径的后面

URL 重写是一种很好的维持 HTTP 会话的方式,它在浏览器不支持 Cookie 时能够很好地工作,但是它的缺点是会动态生成每个 URL 来为页面分配一个 HTTP 会话 Session ID。为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个 Session ID。

隐藏表单域技术就是在表单域添加一个隐藏字段,以便在表单提交时能够把 Session ID 传递回服务器。

利用 Form 表单的隐藏表单域,可以在完全脱离浏览器对Cookie 的使用限制以及在用户无法从页面显示看到隐藏标识的情况下,将标识随请求一起传送给服务器处理,从而实现会话的跟踪。

如果觉得《会话管理:Session与Cookie》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。