Session

session对象
        在web开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务
        session和cookie的主要区别在于:cookie是把用户的数据写给用户的浏览器(保存在客户机);session技术把用户的数据写到用户独占的session中(保存在服务器)。
        session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。

session实现原理
        浏览器A第一次访问Servlet1,服务器会创建一个session,每个session都有一个id号,创建好了后,服务器将id号以cookie的形式回送给客户机(这些是服务器自动完成的)。当浏览器未关闭前再次发请求访问Servlet2时,就会带着这个id号去访问服务器,这时候服务器检索下内存中有没有与之对应的session,有就用这个session为其服务。
        如果想要关掉浏览器再打开还可以使用同一个session,则需要给服务器回送的cookie设置有效时间(服务器自动回送的时候是没有有效期的)。

        具体做法是通过session对象的getId方法获得该session的id,然后创建一个cookie,该cookie的名字为"JSESSIONID",值就是刚刚获得的id,再将该cookie设置下有效期,(也可以设置下Path),并添加到cookie中即可。但是有效期不得超过30分钟,因为浏览器关掉后,session只保存30分钟。

       IndexServlet显示首页,并列出所有书

//首页:列出所有书
public class IndexServlet extends HttpServlet {
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        response.setContentType("text/html;charset=UTF-8");    
        PrintWriter out = response.getWriter();
        
        out.write("本网站有如下书:<br/>");
        
        Set<Map.Entry<String,Book>> set = DB.getAll().entrySet();
        for(Map.Entry<String, Book> me : set) {
            Book book = me.getValue();
            out.write(book.getName() + "<a href='/test/servlet/BuyServlet?id="+book.getId()+"'>购买</a><br/>");
            
        }
 
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        doGet(request, response);
    }
 
}
 
//写一个类来模拟数据库
class DB {
    
    private static Map<String,Book> map = new LinkedHashMap();
    
    //静态代码块中的内容只执行一次,该类在加载时,往map集合中put一系列书,map也需要设置为静态的
    static{
        
        map.put("1", new Book("1", "javaweb开发","老张", "一本好书"));
        map.put("2", new Book("2", "spring开发","老倪", "一本好书"));
        map.put("3", new Book("3", "hibernate开发","老童", "一本好书"));
        map.put("4", new Book("4", "struts开发","老毕", "一本好书"));
        map.put("5", new Book("5", "ajax开发","老张", "一本好书"));
        map.put("6", new Book("6", "java基础","老孙", "一本好书"));
        
    }
    
    public static Map getAll() {
        return map;
    }    
}
 
class Book {
    
    private String id;
    private String name;
    private String author;
    private String description;
                
    public Book() {
    }
 
    public Book(String id, String name, String author, String description) {
        super();
        this.id = id;
        this.name = name;
        this.author = author;
        this.description = description;
    }
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }        
}

      当用户点击购买时,将书的id号带上,并跳转到BuyServlet去处理:

public class BuyServlet extends HttpServlet {
 
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        String id = request.getParameter("id"); //获得url中带过来的参数id
        Book book = (Book)DB.getAll().get(id); //在DB中获得该id号的book
        
        HttpSession session = request.getSession(); //获得当前session对象
        Cookie cookie = new Cookie("JSESSIONID", session.getId()); //设置新的cookie,注意cookie名必须为JSESSIONID,值为该session的id
        cookie.setMaxAge(30*60); //设置cookie有效期
        cookie.setPath("/test"); //设置cookie的路径
        response.addCookie(cookie); //将cookie添加到cookies中带给浏览器,下次浏览器访问,就会将此cookie带过来了
        
        //先把书加到容器里,再把容器加到session中。一般先检查用户的session中有没有保存书的容器,没有就创建,有就加
        List list = (List)session.getAttribute("list");
        if(list == null) {
            list = new ArrayList();
            session.setAttribute("list", list);
        }
        list.add(book);
        
        //跳转到显示用户买过哪些商品
//        request.getRequestDispatcher("/servlet/ListCartServlet").forward(request, response);
        response.sendRedirect("/test/servlet/ListCartServlet");
 
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        doGet(request, response);
    }
}

      从上面的程序可以看出,当用户点击购买后,会将书的id号带过来,我们拿到id号后就可以找到相应的书,同时我们将当前session的id保存到cookie中,再带给浏览器,这样下次浏览器访问的时候就会将当前session的id带过来了。拿到相应的书后,放到list中,再把list放到session中,这样下次跳转的时候,浏览器带来的cookie中有当前session的id,我们可以通过getSession()获得当前的session,再把session中保存的list拿出来,就知道用户买了哪些书了。这就是购物车的原理,请看下面的ListCartServlet:

     

public class ListCartServlet extends HttpServlet {
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        response.setContentType("text/html;charset=UTF-8");    
        PrintWriter out = response.getWriter();
        
        HttpSession session = request.getSession(); //获得当前的session
        List<Book> list = (List)session.getAttribute("list"); //从session中拿出list
        
        if(list == null || list.size() == 0) {
            out.write("对不起,您还没有购买任何商品!");
            return;
        }
        
        out.write("您买过如下商品:<br/>");
        for(Book book : list) {
            out.write(book.getName() + "<br/>");
        }
 
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        doGet(request, response);
    }
}

浏览器禁用cookie后的session处理

        session通过向浏览器回送cookie,如果用户将浏览器的cookie禁用了该如何解决?
        解决方案:url重写,让session的id不以cookie的形式带过来,以url中带过来。有两个url重写的方法:

response.encodeRedirectURL(java.lang.String url);  //用于对sendRedirect方法后的url地址进行重写。
response.encodeURL(java.lang.String url);  //用于对表单action和超链接的url地址进行重写。
两个方法非常的智能,只要发现浏览器发送了任何Cookie过来,就认为当前客户端没有禁用Cookie,就不会在进行URL重写

实现机制为:

  • 先判断当前的 Web 组件是否启用 Session,假如没有,直接返回参数 url。
  • 再判断 客户端浏览器 是否支持 Cookie,假如支持,直接返回参数 url;假如不支持 ,就在参数 url 中加入 Session ID 信息,然后返回修改后的 url。

       URL重写要求将站点中的所有超链接都进行改造,在超链接后用一个特殊的参数JSESSIONID保存当前浏览器对应session的编号,这样一来,当用户点击超链接访问服务器时,服务器可以从URL后的参数中分析出JSESSIONID,从而找到对应的sesison使用.

       在BuyServlet.java中,把session的id号写入cookie的几行代码去掉,将

response.sendRedirect("/test/servlet/ListCartServlet");

       改为:

response.sendRedirect(response.encodeRedirectURL("/test/servlet/ListCartServlet"));

      在IndexServlet.java中,把

out.write(book.getName() + "<a href='/test/servlet/BuyServlet?id="+book.getId()+"'>购买</a><br/>");

      改写成:

String url= "/test/servlet/BuyServlet?id=" + book.getId();
url= response.encodeURL(url);
out.write(book.getName() + "<a href='" + url + "'>购买</a><br/>");

     最后一步很重要:在

   out.write("本网站有如下书:<br/>");之前加上request.getSession();

     因为要实现共享一个session,必须首先获得session。

 

总结
        1. 服务器是如何做到一个session为一个浏览器的多次请求而服务的?
        服务器创建session出来后,会把session的id号以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机带session的id过来了,就会使用内存中与之对应的session为之服务。
        2. 如何做到一个session为多个浏览器服务?
        服务器第一次创建session,程序员把session的id号手动以cookie的形式回送给浏览器,并设置cookie的有效期,这样即使用户的浏览器关了,开新的浏览器时,还会带着session的id号找服务器,服务器从而就可以用内存中与之对应的session为第二个浏览器窗口服务。
        3. 如何做到用户禁用cookie后,session还能为多次请求而服务?
       把用户可能点击的每一个超链接后面,都跟上用户的session id号。
        4. session对象的创建和销毁时机
        用户第一次request.getSession时创建。session对象默认在30分钟没有使用,则服务器会自动销毁session。但是用户可以在web.xml文件中手动配置session的失效时间:  下面表示1分钟失效:

<session-config>
        <session-timeout>1</session-timeout>
</session-config>

 

参考:

https://blog.csdn.net/eson_15/article/details/51261326

posted on 2018-10-13 22:03  溪水静幽  阅读(131)  评论(0)    收藏  举报