zoukankan      html  css  js  c++  java
  • 会话管理(Cookie/Session技术)

    什么是会话:用户打开浏览器,点击多个超链接,访问服务器的多个web资源,然后关闭浏览器,整个过程就称为一个会话;

    会话过程需要解决的问题:每个用户在使用浏览器与服务器进行会话的过程中,都可能会产生一些数据,这些输入如何来进行保存?比如用户在购物网站浏览的商品记录,用户添加购物车的记录等等这些信息如何进行存储?在程序中会话跟踪是一件非常重要的事情,一个用户的所有请求操作都应该属于同一个会话,而另一个人的所有请求操作应该属于另一个人,二者不能混淆!当想到需要在保存数据时,我们首先肯定会想到使用域对象,这些数据是否可以使用Request或者ServletContext对象来保存呢?

    首先我们举例说明:登录的场景

    1 Context对象:

    小张: 输入“张三” (保存数据: context.setAttribute("name","张三")) -> 用户主页(显示“张三”)

    小李: 输入“李四”(保存数据:context.setAttribute("name","李四")) ->   用户主页(显示“李四”)

    context是所有用户公有的资源,因此当小李登录后,用户主页将全部显示为“李四”,新的数据将会覆盖原有的数据,因此不能够使用context对象;

    2 Request对象:该对象只在同一个页面有效,当需要进行页面跳转的时候,显然必须使用转发技术来实现,因此Request对象也不能够有效解决该问题。

     

    会话技术

    为了解决上述的问题,这里引入了会话技术,其中会话技术主要分为两个部分,cookie技术和session技术,前者将数据保存在客户端,后者将数据保存在服务器。

    • Cookie技术:Cookie是客户端技术,服务器把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
    • Session技术:Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。

     

    (一) Cookie技术

    1.1 Cookie技术核心

    Cookie类:用于存储会话数据

    1)构造Cookie对象

    • Cookie(java.lang.String name, java.lang.String value)

    2)设置cookie

    • void setPath(java.lang.String uri)   :设置cookie的有效访问路径
    • void setMaxAge(int expiry) : 设置cookie的有效时间
    • void setValue(java.lang.String newValue) :设置cookie的值

    3)发送cookie到浏览器端保存

    • void response.addCookie(Cookie cookie)  : 发送cookie

    4)服务器接收cookie:

    • Cookie[] request.getCookies()  : 接收cookie,返回所有cookie的数据信息

    1.2 Cookie原理

    1)服务器创建cookie对象,把会话数据存储到cookie对象中。

    • new Cookie("name","value");

    2)服务器发送cookie信息到浏览器

    • response.addCookie(cookie);

    举例: set-cookie: name=Infaraway ( 隐藏发送了一个set-cookie名称的响应头 )

    3)浏览器得到服务器发送的cookie,然后保存在浏览器端。

    4)浏览器在下次访问服务器时,会带着cookie信息

    举例: cookie: name=Infaraway (隐藏带着一个叫cookie名称的请求头)

    5)服务器接收到浏览器带来的cookie信息

    • request.getCookies();
     1 package com.infaraway.servlet;
     2 
     3 import javax.servlet.ServletException;
     4 import javax.servlet.http.Cookie;
     5 import javax.servlet.http.HttpServletRequest;
     6 import javax.servlet.http.HttpServletResponse;
     7 import java.io.IOException;
     8 
     9 /**
    10  * Created by :Infaraway
    11  * DATE : 2017/3/25
    12  * Time : 22:08
    13  * Funtion : cookie测试
    14  */
    15 public class CreateCookie {
    16     public void doGet(HttpServletRequest request, HttpServletResponse response)
    17             throws ServletException, IOException {
    18         //1.创建Cookie对象
    19         Cookie cookie1 = new Cookie("name","Infaraway");
    20         //Cookie cookie2 = new Cookie("email","Infaraway@qq.com");
    21         //Cookie cookie1 = new Cookie("email","Infaraway@qq.com");
    22 
    23         /**
    24          * 1)设置cookie的有效路径。默认情况:有效路径在当前web应用下。 /cookie
    25          */
    26         //cookie1.setPath("/cookie");
    27         //cookie2.setPath("/basisJavaWeb");
    28         /**
    29          * 2)设置cookie的有效时间
    30          * 正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。
    31          * 负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!
    32          * 零:表示删除同名的cookie数据
    33          */
    34         //cookie1.setMaxAge(20); //20秒,从最后不调用cookie开始计算
    35         cookie1.setMaxAge(-1); //cookie保存在浏览器内存(会话cookie)
    36         //cookie1.setMaxAge(0);
    37 
    38         //2.把cookie数据发送到浏览器(通过响应头发送: set-cookie名称)
    39         //response.setHeader("set-cookie", cookie.getName()+"="+cookie.getValue()+",email=eric@qq.com");
    40         //推荐使用这种方法,避免手动发送cookie信息
    41         response.addCookie(cookie1);
    42         //response.addCookie(cookie2);
    43         //response.addCookie(cookie1);
    44 
    45         //3.接收浏览器发送的cookie信息
    46         /*String name = request.getHeader("cookie");
    47         System.out.println(name);*/
    48         Cookie[] cookies = request.getCookies();
    49         //注意:判断null,否则空指针
    50         if(cookies!=null){
    51             //遍历
    52             for(Cookie c:cookies){
    53                 String name = c.getName();
    54                 String value = c.getValue();
    55                 System.out.println(name+"="+value);
    56             }
    57         }else{
    58             System.out.println("没有接收cookie数据");
    59         }
    60     }
    61 }

     

    1.3 Cookie的细节

    1void setPath(java.lang.String uri)   :设置cookie的有效访问路径。有效路径指的是cookie的有效路径保存在哪里,那么浏览器在有效路径下访问服务器时就会带着cookie信息,否则不带cookie信息。

    2void setMaxAge(int expiry) : 设置cookie的有效时间。

    •        正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。
    •        负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!!
    •        零:表示删除同名的cookie数据

    3Cookie数据类型只能保存非中文字符串类型的。可以保存多个cookie,但是浏览器一般只允许存放300Cookie,每个站点最多存放20Cookie,每个Cookie的大小限制为4KB

     

    1.4  案例-查看用户浏览器过的商品

    首先是商品的实体类:

     1 package com.infaraway.entity;
     2 
     3 
     4 /**
     5  * Created by :Infaraway
     6  * DATE : 2017/3/25
     7  * Time : 14:34
     8  * Funtion : 商品类
     9  */
    10 public class Product {
    11 
    12     private String id;
    13     private String proName;
    14     private String proType;
    15     private double price;
    16     public String getId() {
    17         return id;
    18     }
    19     public void setId(String id) {
    20         this.id = id;
    21     }
    22     public String getProName() {
    23         return proName;
    24     }
    25     public void setProName(String proName) {
    26         this.proName = proName;
    27     }
    28     public String getProType() {
    29         return proType;
    30     }
    31     public void setProType(String proType) {
    32         this.proType = proType;
    33     }
    34     public double getPrice() {
    35         return price;
    36     }
    37     public void setPrice(double price) {
    38         this.price = price;
    39     }
    40     public Product(String id, String proName, String proType, double price) {
    41         super();
    42         this.id = id;
    43         this.proName = proName;
    44         this.proType = proType;
    45         this.price = price;
    46     }
    47     public Product() {
    48         super();
    49         // TODO Auto-generated constructor stub
    50     }
    51     @Override
    52     public String toString() {
    53         return "Product [id=" + id + ", price=" + price + ", proName="
    54                 + proName + ", proType=" + proType + "]";
    55     }
    56     
    57 }
    View Code

    这里我们使用list集合来模拟数据库,使用dao层进行增删改查操作

     1 package com.infaraway.dao;
     2 
     3 
     4 import com.infaraway.entity.Product;
     5 
     6 import java.util.ArrayList;
     7 import java.util.List;
     8 
     9 
    10 /**
    11  * Created by :Infaraway
    12  * DATE : 2017/3/25
    13  * Time : 14:34
    14  * Funtion : 商品的增删改查操作
    15  */
    16 public class ProductDao {
    17     //模拟数据库,存储所有的商品信息
    18     private static List<Product> data = new ArrayList<Product>();
    19     
    20     /**
    21      * 静态代码块,初始化
    22      */
    23     static{
    24         //初始化
    25         for(int i=1;i<=10;i++){
    26             data.add(new Product(""+i,"笔记本"+i,"LN00"+i,334.0+i));
    27         }
    28     }
    29 
    30     
    31     /**
    32      * 显示所有的商品
    33      */
    34     public List<Product> findAll(){
    35         return data;
    36     }
    37     
    38     /**
    39      * 根据id查找商品
    40      */
    41     public Product findById(String id){
    42         for(Product p:data){
    43             if(p.getId().equals(id)){
    44                 return p;
    45             }
    46         }
    47         return null;
    48     }
    49 
    50 }
    View Code

    然后给出商品列表界面,由用户点击选择浏览的商品,并且给出了用户的浏览记录

     1 package com.infaraway.servlet;
     2 
     3 import com.infaraway.dao.ProductDao;
     4 import com.infaraway.entity.Product;
     5 
     6 import javax.servlet.ServletException;
     7 import javax.servlet.http.Cookie;
     8 import javax.servlet.http.HttpServlet;
     9 import javax.servlet.http.HttpServletRequest;
    10 import javax.servlet.http.HttpServletResponse;
    11 import java.io.IOException;
    12 import java.io.PrintWriter;
    13 import java.util.List;
    14 
    15 /**
    16  * Created by :Infaraway
    17  * DATE : 2017/3/25
    18  * Time : 14:34
    19  * Funtion :
    20  */
    21 public class ListServlet extends HttpServlet {
    22     @Override
    23     public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    24 
    25         resp.setContentType("text/html;charset=utf-8");
    26         ProductDao dao = new ProductDao();
    27         List<Product> productList = dao.findAll();
    28 
    29         //2.把商品显示到浏览器
    30         PrintWriter writer = resp.getWriter();
    31         String html = "";
    32 
    33         html += "<html>";
    34         html += "<head>";
    35         html += "<title>显示商品列表</title>";
    36         html += "</head>";
    37         html += "<body>";
    38         html += "<table border='1' align='center' width='600px'>";
    39         html += "<tr>";
    40         html += "<th>编号</th><th>商品名称</th><th>商品型号</th><th>商品价格</th>";
    41         html += "</tr>";
    42         //遍历商品
    43         if(productList!=null){
    44             for(Product p:productList){
    45                 html += "<tr>";
    46                 // /day11_hist/DetailServlet?id=1 访问DetailSErvlet的servlet程序,同时传递 名为id,值为1 的参数
    47                 html += "<td>"+p.getId()+"</td><td><a href='"+req.getContextPath()+"/DetailServlet?id="+p.getId()+"'>"+p.getProName()+"</a></td><td>"+p.getProType()+"</td><td>"+p.getPrice()+"</td>";
    48                 html += "<tr>";
    49             }
    50         }
    51         html += "</table>";
    52 
    53 
    54         /**
    55          * 显示浏览过的商品
    56          */
    57         html += "最近浏览过的商品:<br/>";
    58         //取出prodHist的cookie
    59         Cookie[] cookies = req.getCookies();
    60         if(cookies!=null){
    61             for (Cookie cookie : cookies) {
    62                 if(cookie.getName().equals("prodHist")){
    63                     String prodHist = cookie.getValue(); // 3,2,1
    64                     String[] ids = prodHist.split(",");
    65                     //遍历浏览过的商品id
    66                     for (String id : ids) {
    67                         //查询数据库,查询对应的商品
    68                         Product p = dao.findById(id);
    69                         //显示到浏览器
    70                         html += ""+p.getId()+"&nbsp;"+p.getProName()+"&nbsp;"+p.getPrice()+"<br/>";
    71                     }
    72                 }
    73             }
    74         }
    75 
    76 
    77         html += "</body>";
    78         html += "</html>";
    79 
    80         writer.write(html);
    81 
    82     }
    83 
    84     @Override
    85     public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    86         super.doGet(req, resp);
    87     }
    88 }
    View Code

    最后给出商品详情界面

      1 package com.infaraway.servlet;
      2 
      3 
      4 import com.infaraway.dao.ProductDao;
      5 import com.infaraway.entity.Product;
      6 
      7 import java.io.IOException;
      8 import java.io.PrintWriter;
      9 import java.util.Arrays;
     10 import java.util.Collection;
     11 import java.util.LinkedList;
     12 
     13 import javax.servlet.ServletException;
     14 import javax.servlet.http.Cookie;
     15 import javax.servlet.http.HttpServlet;
     16 import javax.servlet.http.HttpServletRequest;
     17 import javax.servlet.http.HttpServletResponse;
     18 /**
     19  * Created by :Infaraway
     20  * DATE : 2017/3/25
     21  * Time : 14:34
     22  * Funtion : 商品信息详细页面
     23  */
     24 public class DetailServlet extends HttpServlet {
     25 
     26     public void doGet(HttpServletRequest request, HttpServletResponse response)
     27             throws ServletException, IOException {
     28         response.setContentType("text/html;charset=utf-8");
     29         //1.获取传来的商品id
     30         String id = request.getParameter("id");
     31         
     32         //2. 创建dao,进行商品的操作
     33         ProductDao dao = new ProductDao();
     34         Product product = dao.findById(id);
     35         
     36         //3.将商品信息打印到浏览器页面
     37         PrintWriter writer = response.getWriter();
     38         String html = "";
     39         
     40         html += "<html>";
     41         html += "<head>";
     42         html += "<title>商品详情</title>";
     43         html += "</head>";
     44         html += "<body>";
     45         html += "<table border='1' align='center' width='300px'>";
     46         if(product!=null){
     47             html += "<tr><th>编号:</th><td>"+product.getId()+"</td></tr>";
     48             html += "<tr><th>商品名称:</th><td>"+product.getProName()+"</td></tr>";
     49             html += "<tr><th>商品类型:</th><td>"+product.getProType()+"</td></tr>";
     50             html += "<tr><th>商品价格:</th><td>"+product.getPrice()+"</td></tr>";
     51         }
     52         
     53         html += "</table>";
     54         html += "<center><a href='"+request.getContextPath()+"/ListServlet'>[返回列表]</a></center>";
     55         html += "</body>";
     56         html += "</html>";
     57         
     58         writer.write(html);
     59         
     60         
     61         /**
     62          * 将浏览过的商品存入cookie
     63          */
     64         //1.创建cookie
     65         Cookie cookie = new Cookie("prodHist",createValue(request,id));
     66         cookie.setMaxAge(1*30*24*60*60);//设置cookie有效时间(单位:s)
     67         //2.保存cookie
     68         response.addCookie(cookie);
     69     }
     70 
     71     /**
     72      * 生成cookie的值信息
     73      *         当前cookie值  -->   传入的商品id      -->         最终的cookie值
     74      *     null或没有prodHist          1                     1    (算法: 直接返回传入的id )
     75      *             1                  2                     2,1 (没有重复且小于3个。算法:直接把传入的id放最前面 )
     76      *             2,1                1                     1,2(有重复且小于3个。算法:去除重复id,把传入的id放最前面 )
     77      *             3,2,1              2                     2,3,1(有重复且3个。算法:去除重复id,把传入的id放最前面)
     78      *             3,2,1              4                     4,3,2(没有重复且3个。算法:去最后的id,把传入的id放最前面)
     79      */
     80     private String createValue(HttpServletRequest request,String id) {
     81         
     82         Cookie[] cookies = request.getCookies();
     83         String prodHist = null;
     84         if(cookies!=null){
     85             for (Cookie cookie : cookies) {
     86                 if(cookie.getName().equals("prodHist")){
     87                     prodHist = cookie.getValue();
     88                     break;
     89                 }
     90             }
     91         }
     92 
     93 
     94         // null或没有prodHist
     95         if(cookies==null || prodHist==null){
     96             //直接返回传入的id
     97             return id;
     98         }
     99 
    100         // 3,21          2
    101         //String -> String[] ->  Collection :为了方便判断重复id
    102         String[] ids = prodHist.split(",");
    103         Collection<String> colls = Arrays.asList(ids); //<3,21>
    104         // LinkedList 方便地操作(增删改元素)集合
    105         // Collection -> LinkedList
    106         LinkedList<String> list = new LinkedList<String>(colls);
    107 
    108 
    109         //不超过3个
    110         if(list.size()<3){
    111             //id重复
    112             if(list.contains(id)){
    113                 //去除重复id,把传入的id放最前面
    114                 list.remove(id);
    115                 list.addFirst(id);
    116             }else{
    117                 //直接把传入的id放最前面
    118                 list.addFirst(id);
    119             }
    120         }else{
    121             //等于3个
    122             //id重复
    123             if(list.contains(id)){
    124                 //去除重复id,把传入的id放最前面
    125                 list.remove(id);
    126                 list.addFirst(id);
    127             }else{
    128                 //去最后的id,把传入的id放最前面
    129                 list.removeLast();
    130                 list.addFirst(id);
    131             }
    132         }
    133 
    134         // LinedList -> String
    135         StringBuffer sb = new StringBuffer();
    136         for (Object object : list) {
    137             sb.append(object+",");
    138         }
    139         //去掉最后的逗号
    140         String result = sb.toString();
    141         result = result.substring(0, result.length()-1);
    142         return result;
    143     }
    144 
    145     public void doPost(HttpServletRequest request, HttpServletResponse response)
    146             throws ServletException, IOException {
    147         doGet(request, response);
    148     }
    149 
    150 }
    View Code

    上述代码可以在这里找到:https://git.oschina.net/infaraway/basisJavaWeb/tree/master/cookie

     

    (二) Session技术

    2.1  引入

    Cookie的局限:

    • 1)Cookie只能存字符串类型。不能保存对象
    • 2)只能存非中文。
    • 3)1个Cookie的容量不超过4KB。

    如果要保存非字符串,超过4kb内容,只能使用session技术!

    Session特点:会话数据保存在服务器端。(内存中)

    2.2 Session技术核心

    HttpSession类:用于保存会话数据

    1)创建或得到session对象

    • HttpSession getSession()
    • HttpSession getSession(boolean create)

    2)设置session对象

    • void setMaxInactiveInterval(int interval) : 设置session的有效时间
    • void invalidate() : 销毁session对象
    • java.lang.String getId() : 得到session编号

    3)保存会话数据到session对象

    • void setAttribute(java.lang.String name, java.lang.Object value) : 保存数据
    • java.lang.Object getAttribute(java.lang.String name) : 获取数据
    • void removeAttribute(java.lang.String name) : 清除数据

    2.3 Session原理

    问题: 服务器能够识别不同的浏览者!

    关键: 在哪个session域对象保存数据,就必须从哪个域对象取出!

    代码解读:HttpSession session = request.getSession();

    1)第一次访问创建session对象,给session对象分配一个唯一的ID,叫JSESSIONID

      new HttpSession();

    2)把JSESSIONID作为Cookie的值发送给浏览器保存

      Cookie cookie = new Cookie("JSESSIONID", sessionID);

      response.addCookie(cookie);

    3)第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器

    4)服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。
    if(找到){
      return map.get(sessionID);
    }
    Map<String,HttpSession>


    5)如果找到对应编号的session对象,直接返回该对象

    6)如果找不到对应编号的session对象,创建新的session对象,继续走1的流程

    结论:通过JSESSION的cookie值在服务器找session对象!

     1 import java.io.IOException;
     2 
     3 import javax.servlet.ServletException;
     4 import javax.servlet.http.Cookie;
     5 import javax.servlet.http.HttpServlet;
     6 import javax.servlet.http.HttpServletRequest;
     7 import javax.servlet.http.HttpServletResponse;
     8 import javax.servlet.http.HttpSession;
     9 /**
    10  * Created by :Infaraway
    11  * DATE : 2017/3/25
    12  * Time : 14:34
    13  * Funtion : session对象的创建
    14  */
    15 public class CreateSession extends HttpServlet {
    16 
    17     public void doGet(HttpServletRequest request, HttpServletResponse response)
    18             throws ServletException, IOException {
    19         //1.创建session对象
    20         HttpSession session = request.getSession();
    21         //得到session编号
    22         System.out.println("id="+session.getId());
    23         //修改session的有效时间
    24         //session.setMaxInactiveInterval(20);
    25 
    26         //手动发送一个硬盘保存的cookie给浏览器
    27         Cookie c = new Cookie("JSESSIONID",session.getId());
    28         c.setMaxAge(60*60);
    29         response.addCookie(c);
    30 
    31         //2.保存会话数据
    32         session.setAttribute("name", "Infaraway");
    33     }
    34 }
     1 package com.infaraway.servlet;
     2 
     3 import javax.servlet.ServletException;
     4 import javax.servlet.http.Cookie;
     5 import javax.servlet.http.HttpServlet;
     6 import javax.servlet.http.HttpServletRequest;
     7 import javax.servlet.http.HttpServletResponse;
     8 import java.io.IOException;
     9 
    10 /**
    11  * Created by :Infaraway
    12  * DATE : 2017/3/25
    13  * Time : 22:17
    14  * Funtion :
    15  */
    16 public class DeleteCookie extends HttpServlet {
    17 
    18     public void doGet(HttpServletRequest request, HttpServletResponse response)
    19             throws ServletException, IOException {
    20         /**
    21          * 需求: 删除cookie
    22          */
    23         Cookie cookie = new Cookie("name","xxxx");
    24         cookie.setMaxAge(0);//删除同名的cookie
    25         response.addCookie(cookie);
    26         System.out.println("删除成功");
    27 
    28     }
    29 }

    2.4  Sesson细节

    1)java.lang.String getId() : 得到session编号

    2)两个getSession方法:

    • getSession(true) / getSession() : 创建或得到session对象。没有匹配的session编号,自动创 建新的session对象。
    • getSession(false): 得到session对象。没有匹配的session编号,返回null

    3)void setMaxInactiveInterval(int interval) : 设置session的有效时间

    session对象销毁时间:

    • 1 默认情况30分服务器自动回收
    • 2 修改session回收时间
    • 3 全局修改session有效时间
    1 <!-- 修改session全局有效时间:分钟 -->
    2 <session-config>
    3   <session-timeout>1</session-timeout>
    4 </session-config>
    • 4.手动销毁session对象

        void invalidate() : 销毁session对象

    4)如何避免浏览器的JSESSIONID的cookie随着浏览器关闭而丢失的问题?

      浏览器关闭而丢失cookie的原因是cookie的有效时间设置中参数为负整数导致,因此需求使用setMaxAge()函数将时间修正为正整数即可。

    1 /**
    2 * 手动发送一个硬盘保存的cookie给浏览器
    3 */
    4 Cookie c = new Cookie("JSESSIONID",session.getId());
    5 c.setMaxAge(60*60);
    6 response.addCookie(c);

     

    上述代码可以在这里找到:https://git.oschina.net/infaraway/basisJavaWeb/tree/master/session

     

     

    总结:

    1)会话管理: 浏览器和服务器会话过程中的产生的会话数据的管理。

    2)Cookie技术:

    • new Cookie("name","value")
    • response.addCookie(coookie)
    • request.getCookies()

    3)Session技术

    • request.getSession();
    • setAttrbute("name","会话数据");
    • getAttribute("会话数据")
  • 相关阅读:
    类数组对象与arguments
    bind的模拟实现
    new的模拟实现
    call和apply的模拟实现
    参数按值传递
    闭包
    执行上下文
    ECMAScript规范解读this
    缓存使用-8、redis的缓存穿透和缓存雪崩
    缓存使用-7、Redis 为什么是单线程的
  • 原文地址:https://www.cnblogs.com/infaraway/p/6618888.html
Copyright © 2011-2022 走看看