一、Cookie
二、Session
从用户进入一个网站浏览到退出这个网站或者关闭浏览器称为一次会话。会话跟踪指在这个过程中浏览器与服务器的多次请求保持数据共享的状态的技术。从而管理着浏览器客户端和服务端之间会话过程中产生的数据。常用的会话跟踪技术是Cookie与Session。
- cookie技术:会话数据保存在客户端
- session技术:会话数据保存在服务端
一、Cookie
Cookie,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密),可以叫做浏览器缓存。Cookie 是服务器在客户端上存储的小段文本并随每一个请求发送至同一个服务器。
Cookie 的内容主要包括:名字,值,有效时间,路径和域。
- 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
- 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
(一)Cookie类
javax.servlet.http.Cookie类有一系列的方法来对对Cookie进行相应的操作。
HttpServletResponse接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。
同样,HttpServletRequest接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。
//1、构造对象
public Cookie(String name, String value)
//2、设置Cookie
//设置cookie的有效访问路径
public void setPath(String uri)
//设置cookie的有效访问时间
public void setMaxAge(int expiry)
//设置cookie的值
public void setValue(String newValue)
//设置cookie的域
public void setDomain(String domain)
//设置cookie是否以安全的协议传输
public void setSecure(boolean flag)
//3、发送cookie到浏览器端保存
void resp.addCookie(Cookie cookie)
//4、服务器接受cookies
Cookie res.getCookies()
(二)cookie的生命周期
如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。
若希望浏览器将该cookie存储在磁盘上,则需要设置cookie的有效时间,并给出一个以秒为单位的时间。当设置了有效时间的话,cookie会被保存在硬盘当中。
public void setMaxAge(int expiry)
/*
expiry参数,表示保存的时间,时间过去后,浏览器会自动删除该cookie
正整数:表示cookie数据保存浏览器的缓存目录(硬盘)中指定的时间
负整数:标书cookie数据保存浏览器的内存中指定的时间,而且浏览器关闭cookie就丢失
零:表示删除同名的cookie的数据
*/
(三)cookie域名和路径
路径与域一起构成 cookie 的作用范围。
1、Cookie是不可跨域名的。
不同域名颁发的Cookie不会被提交到另外一个域名去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。
//例如,想要 happy.com域名下的二级域名都访问使用同一个cookie,可以通过设置cookie的域的来实现
Cookie cookie = new Cookie("name","value");
cookie.setDomain(".happy.com");
resp.addCookie(cookie);
//这样,二级域名 如 happy.com/example1 和happy.com/example2 两个域名都会访问同一个Cookie
//注意:domain参数必须以点(".")开始。
2、路径
而path属性决定允许访问Cookie的路径。有效路径指的是cookie保存的地方,那么浏览器在有效路径下访问服务器就会带着该cookie信息。
//设置cookie的有效访问路径
public void setPath(String uri)
(四)Cookie的安全属性和局限性
HTTP协议不仅是无状态的,而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。使用HTTP协议传输很机密的内容是一种隐患。如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。
//Indicates to the browser whether the cookie should only be sent using a secure protocol, such as HTTPS or SSL.The default value is false.
public void setSecure(boolean flag)
2、Cookie的局限性
- cookie只能存储字符串类型,不能保存对象
- 只能存储非中文字符串,储存中文只能以编码的格式来储存
- 1个cookie的容量不能超过4kb
(五)案例一:显示用户上次访问时间
是由部署在tomcat的Servlet程序实现。
package cookie;
import java.io.IOException;
import java.util.Date;
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;
/**
* Description:
* 利用cookie技术显示用户的上一次访问时间
*
* @author lee
*/
@WebServlet("/ShowTime")
public class ShowTime extends HttpServlet {
/**
* Descriptin:
* 利用cookie技术显示用户的上一次访问时间
*
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取当前时间
String currentTime = new Date().toString();
String lastTime = "";
//获取客户端的cookies
//an array of all the Cookies included with this request, or null if the request has no cookies
Cookie[] cookies = request.getCookies();
//假如客户端的cookies长度为0,则表示该用户第一次访问,否则为二次访问
if(cookies == null){
response.getWriter().write("第一次访问,当前时间为"+currentTime);
}else{
//for循环方法,是一定在在数组不为null的情况下使用的
for(Cookie cookie:cookies){
if(cookie.getName().equals("lastTime")){
//获取用户上次访问时间
lastTime = cookie.getValue();
}
}
response.getWriter().write("欢迎再度访问,上次访问时间为:"
+lastTime
+"当前时间为:"+currentTime);
}
//利用cookie保存当前访问时间
Cookie cookie = new Cookie("lastTime",currentTime);
//设置cookie的有效时间
cookie.setMaxAge(1*60*60*24);
//将cookie发送给客户端
response.addCookie(new Cookie("lastTime",currentTime));
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
(五)案例二:显示用户浏览的商品记录
是由部署在tomcat的Servlet程序实现。
(1)我们实现了实体Product类,封装着商品的信息
(2)DAO(Data Access Object,数据访问对象是一个面向对象的数据库接口,项目原则:通常一个实体对象就会对应一个DAO接口和一个DAO实现类)
为了简化程序,没有从数据库中去提取数据。而是,直接用ProducePro类来产生10个Product实例
(3)servlet服务器端程序,ProList动态地显示所有商品和历史浏览商品。ProDetial动态地显示商品的详细信息
1、entity.Product
package cookie.entity;
/**
* Description:
* 商品类,封装着商品的信息。
*
*
* @author lee
* */
public class Product {
//商品的id编号
int id;
//商品的名字
String name;
//商品的价格
int price;
//商品的介绍
String introduce;
//可以补充更多信息...
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getIntroduce() {
return introduce;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
}
2、dao.ProducePro
package cookie.dao;
import java.util.ArrayList;
import cookie.entity.*;
/**
* Description:
* 一个单例类,用来产生Product类的实例。
*
* 这里为了简化程序,没有从数据库中去提取数据。
* 而是,直接用ProducePro类来产生10个Product实例(即是10个商品)
*
* @author lee
* */
public class ProducePro {
//静态初始化该类,实现单例设计,关于单例设计模式可以看我另外一篇博客
public static ProducePro producePro = new ProducePro();
//用来存放商品实例的集合
ArrayList<Product> products = null;
/**
* Description:
* 私有化构造器,初试化10个商品对象
*
* */
private ProducePro(){
products = new ArrayList<>();
for(int i=0;i<10;i++){
Product pro = new Product();
pro.setId(i);
pro.setName("商品"+i);
pro.setPrice(new Random().nextInt(100));
pro.setIntroduce("Actually, this is a detailed introduce for "+"商品"+i);
products.add(pro);
}
}
/**
* Description:
* 获取ProducePro的实例
* */
public static ProducePro getInstance(){
return producePro;
}
/**
* Description:
* 获取所有的商品
* */
public ArrayList<Product> findAll(){
return this.products;
}
/**
* Description:
* 根据指定的编号来获取指定的商品
*
* */
public Product findProById(int id){
return products.get(id);
}
//可以补充更多方法...
}
3、servlet.ProList
package cookie.servlet;
import java.io.IOException;
import java.util.ArrayList;
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 cookie.dao.ProducePro;
import cookie.entity.Product;
/**
* Description:
* 将所有商品以列表的形式展示出来。
* 获取用户的cookie,假如有Sawpro cookie记录着用户的历史访问记录,
* 则展示用户的历史访问记录。
*
* 这里,设定一个名为 Sawpro的cookie,它的值为用户所浏览过的商品的编号用“,”符号连接在一起的字符串
* 例如:1,2,3,则表示用户访问过 1,2,3编号的商品。之存储3个历史记录,并且最近的浏览商品会展示在最第一个。
*
* @author lee
*/
@WebServlet("/ProList")
public class ProList extends HttpServlet {
/**
* Description:
* 创建一个商品列表和用户历史访问列表。
*
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应的内容类型和编码格式
response.setContentType("text/html;charset=utf-8");
//获取所有商品
ProducePro pp = ProducePro.getInstance();
ArrayList<Product> ps = pp.findAll();
//1、显示所有商品
StringBuffer content = new StringBuffer();
content.append("<html>"
+ "<head><title>商品列表</title></head>"
+ "<body><h3>所有商品</h3><br><table>"
+ "<tr>"
+ "<th>商品编号</th>"
+ "<th>商品名字</th>"
+ "<th>商品价格</th>"
+ "</tr>");
//遍历所有商品实例
for(Product p : ps){
content.append(
"<tr>"
+ "<th>"+p.getId()+"</th>"
//给商品的名字设置<a>标签,当用户点击时,则会进入ProDetail Servlet展示商品的详细信息
//通过请求参数来传递商品的编号
+ "<th><a href='/webDemo/ProDetail?id="+p.getId()+"'>"+p.getName()+"</a></th>"
+ "<th>"+p.getPrice()+"</th>"
+ "</tr>");
}
content.append("</table><br>");
//2、显示用户历史浏览记录,假如没有则不显示
//历史浏览过的商品的编号,只存储三个历史浏览记录
String[] pros = new String[3];
//获得客户端的cookie
Cookie[] cookies = request.getCookies();
//当cookie不为null,遍历cookie寻找历史浏览记录Sawpro
if(cookies!=null){
for(Cookie cookie:cookies){
if(cookie.getName().equals("Sawpro")){
//这里获取的历史浏记录是,历史浏览的商品编号用冒号连接
//因此用冒号来分割,获取所有历史浏览商品的编号
String[] sawpro = cookie.getValue().split(",");
for(int i =0;i<sawpro.length;i++){
//只储三个历史记录
if(i==3)
break;
pros[i] = sawpro[i];
}
//显示所有历史浏览记录
content.append("<h4>历史浏览商品</h4><table>"
+ "<tr>"
+ "<th>商品编号</th>"
+ "<th>商品名字</th>"
+ "<th>商品价格</th>"
+ "</tr>");
//遍历所有历史浏览商品
for(int i=0;i<pros.length;i++){
if(pros[i]!=null){
//根据编号来获取封装着商品详细信息的实例
Product pro = pp.findProById(Integer.parseInt(pros[i]));
//显示
content.append(
"<tr>"
+ "<th>"+pro.getId()+"</th>"
+ "<th>"+pro.getName()+"</th>"
+ "<th>"+pro.getPrice()+"</th>"
+ "</tr>");
}
}
content.append("</table>");
break;
}
}
}
content.append("</body></html>");
response.getWriter().write(content.toString());
}
/**
* Description:
* 将所有post请求,由doGet方法来处理
* */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
4、servlet.ProDetail
package cookie.servlet;
import java.io.IOException;
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 cookie.dao.ProducePro;
import cookie.entity.Product;
/**
* Description:
* 显示指定商品的详细信息,并且获取用户的之前的访问记录,和本次访问记录储存在cookie中,发送给用户。
*
*
* 这里,设定一个名为 Sawpro的cookie,它的值为用户所浏览过的商品的编号用“,”符号连接在一起的字符串
* 例如:1,2,3,则表示用户访问过 1,2,3编号的商品。之存储3个历史记录,并且最近的浏览商品会展示在最第一个。
*
* @author lee
*/
@WebServlet("/ProDetail")
public class ProDetail extends HttpServlet {
/**
* Description:
* 显示指定商品的详细信息,并且获取用户的之前的访问记录,和本次访问记录储存在cookie中,发送给用户。
*
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应的内容类型和编码格式
response.setContentType("text/html;charset=utf-8");
//1、显示指定商品的详细信息
//获取从ProList传递过来的商品编号
String id = request.getParameter("id");
//根据商品编号获取相应的商品的详细信息
ProducePro pp = ProducePro.getInstance();
Product p = pp.findProById(Integer.parseInt(id));
//显示指定商品的详细内容
StringBuffer content = new StringBuffer();
content.append("<html>"
+ "<head><title>商品详细介绍</title></head>"
+ "<body><table>"
+ "<tr>"
+ "<th>商品编号</th>"
+ "<th>商品名字</th>"
+ "<th>商品价格</th>"
+ "<th>商品介绍</th>"
+ "</tr>"
+ "<tr>"
+ "<th>"+p.getId()+"</th>"
+ "<th>"+p.getName()+"</th>"
+ "<th>"+p.getPrice()+"</th>"
+ "<th>"+p.getIntroduce()+"</th>"
+ "</tr>");
//返回商品目录
content.append("</table><br><a href='/webDemo/ProList'>回到商品目录</a></body></html>");
response.getWriter().write(content.toString());
//2、获取用户的之前的访问记录,和本次访问记录储存在cookie中,发送给用户
//历史浏览过的商品的编号,只存储三个历史浏览记录
String[] pros = new String[3];
//获取客户端的cookie
Cookie[] cookies = request.getCookies();
if(cookies!=null){
for(Cookie cookie : cookies){
//获取之前用户的历史浏览记录
if(cookie.getName().equals("Sawpro")){
//这里获取的历史浏记录是,历史浏览的商品编号用冒号连接
//因此用冒号来分割,获取所有历史浏览商品的编号
String[] sawpro = cookie.getValue().split(",");
//之前历史浏览过的商品的编号,添加到历史浏览过的商品
for(int i=0;i<sawpro.length;i++){
//只储存三个历史记录
if(i==2)
break;
pros[i+1] = sawpro[i];
}
}
break;
}
}
//将当前浏览的商品编号添加在历史浏览当中,并且处于第一个
pros[0] = id;
//将cookie发送给用户
response.addCookie(new Cookie("Sawpro",setCookieValue(pros)));
}
/**
* Description:
* 将String数组的元素,按照指定的格式组成一个字符串
*
* */
public String setCookieValue(String[] pros){
String value = "";
String last = pros[0];
for(int i=0;i<pros.length;i++){
if(pros[i]!=null){
//排除同样编号的商品
if(i>0&&last.equals(pros[i]))
continue;
value += pros[i]+",";
}
}
return value;
}
// public void print(String[] a){
// String aaa = "";
// for(String aa:a){
// aaa +="["+aa+"]";
// }
// System.out.println(aaa);
// }
/**
* Description:
* 将所有post请求,由doGet方法来处理
* */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
二、Session
session采用的是一种在服务器端保持状态的解决方案。
即是服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象(默认情况下,一个浏览器独占一个session对象)。
由于session为用户浏览器独享,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
-
(一)Session的机制
-
1)创建session对象,给session对象分配一个唯一的ID,叫JSEESIONID
2)把JSEESIONID作为Cookie的值发送给浏览器保存
3)第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器
4)服务器得到JSEESIONID,在服务器的内存中搜索是否存放着对应编号的session对象
5)如果找得到对象编号的session对象,直接返回对象
6)如果找不到到对应编号的session对象 ,则创建新的session对象
注意:
1、session借助于 cookie 机制来达到保存标识的目的。当我们想服务器请求创建一个session的时候,服务器会在响应头Set-Cookie中包含session的标识JSESSIONID,其值就是唯一的标识。该cookie是保存内存当中(即是退
出该浏览器或者打开新的浏览器该标记就会消失)因此,我们可以覆盖该cookie,将session的标记保存在硬盘当中。
2、 Session 中能够存取任何类型的数据。
(二)HttpSession
//1、从HttpServletRequest方法中得到session
//假如找不session对象,则创建一个新的session
HttpSession getSession()
//参数create为false的时候,假如找不session对象,返回null
HttpSession getSession(boolean create)
//2、session的属性
//设置有效时间
void setMaxInactiveInterval(int interval)
//获取有效时间
int getMaxInactiveInterval()
//获取sessin的唯一标识
String getId()
//3、设置session的参数
//设置参数
void setAttribute(String name, Object value)
//获取参数
Object getAttribute(String name)
//获取所有参数的名字的枚举
Enumeration<String> getAttributeNames()
//移除指定的参数
void removeAttribute(String name)
(三)session的生命周期
假如没有通过setMaxInactiveInterval方法来设置session的有效时间,默认情况下30分钟服务器(这里使用tomcat服务器)自动回收。
也可以修改web.xml配置来修改session的有效时间(全局)
<!--一分钟后销毁该session对象-->
<session-config>
<session-timeout>1</session-timeout>
<session-config>