JavaWeb基础之Cookie与Session

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

本文内容

1.javax.servlet.http.Cookie

Java提供操作Cookie的API

1.1 演示Cookie的创建与使用

public class CookieDemo1 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //1.创建Cookie对象
        Cookie cookie1 = new Cookie("name","eric");
        
        
        /**
         * 1)设置cookie的有效路径。默认情况:有效路径在当前web应用下。 /demo
               *有效路径指的是cookie的有效路径保存在哪里,那么浏览器在有效路径下
              *访问服务器时就会带着cookie信息,否则不带cookie信息。
         */
        //cookie1.setPath("/demo");
        
        /**
         * 2)设置cookie的有效时间
         * 正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。
            负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!!
            零:表示删除同名的cookie数据

         */
        //cookie1.setMaxAge(20); //20秒,从最后不调用cookie开始计算
        cookie1.setMaxAge(-1); //cookie保存在浏览器内存(会话cookie)
        //cookie1.setMaxAge(0);
        
        
        //2.把cookie数据发送到浏览器(通过响应头发送: set-cookie名称)
        //response.setHeader("set-cookie", cookie.getName()+"="+cookie.getValue()+",email=eric@qq.com");
        //推荐使用这种方法,避免手动发送cookie信息
        response.addCookie(cookie1);
        

        
        //3.接收浏览器发送的cookie信息
        /*String name = request.getHeader("cookie");
        System.out.println(name);*/
        Cookie[] cookies = request.getCookies();
        //注意:判断null,否则空指针
        if(cookies!=null){
            //遍历
            for(Cookie c:cookies){
                String name = c.getName();
                String value = c.getValue();
                System.out.println(name+"="+value);
            }
        }else{
            System.out.println("没有接收cookie数据");
        }
        
    }

}

1.2. Cookie的使用实例,用户上次访问时间

public class HistServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");

        // 获取当前时间
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String curTime = format.format(new Date());

        // 取得cookie
        Cookie[] cookies = request.getCookies();
        String lastTime = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("lastTime")) {
                    // 有lastTime的cookie,已经是第n次访问
                    lastTime = cookie.getValue();// 上次访问的时间
                    // 第n次访问
                    // 1.把上次显示时间显示到浏览器
                    response.getWriter().write(
                            "欢迎回来,你上次访问的时间为:" + lastTime + ",当前时间为:" + curTime);
                    // 2.更新cookie
                    cookie.setValue(curTime);
                    cookie.setMaxAge(1 * 30 * 24 * 60 * 60);
                    // 3.把更新后的cookie发送到浏览器
                    response.addCookie(cookie);
                    break;
                }
            }
        }

        /**
         * 第一次访问(没有cookie 或 有cookie,但没有名为lastTime的cookie)
         */
        if (cookies == null || lastTime == null) {
            // 1.显示当前时间到浏览器
            response.getWriter().write("你是首次访问本网站,当前时间为:" + curTime);
            // 2.创建Cookie对象
            Cookie cookie = new Cookie("lastTime", curTime);
            cookie.setPath("/CookieDemo");
            cookie.setMaxAge(-1);// 设置浏览器关闭cookie就丢失
            // cookie.setMaxAge(1*30*24*60*60);//保存一个月
            // 3.把cookie发送到浏览器保存
            response.addCookie(cookie);
        }
    }

}

效果如下:


**第一次访问时效果如下,可以看到请求头里没有带任何Cookie信息,而响应头里面设置了Cookie的信息**
**当第二次及后面的访问时,请求头里面都会带有保存上次访问的Cookie信息,响应头里面会对这个信息进行更新。**
**同时我们也可以在浏览器里看到该Cookie的详细信息**

注意:浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

2. javax.servlet.http.HttpSession

Java提供操作Session的API,服务器在浏览器第一次访问时如果创建了一个Session对象就会给该对象分配一个id并以Cookie的形式保存在浏览器中,以后在有效期内浏览器访问服务器时就会带着该id,服务器通过该id就能匹配到存储在服务器中对应的信息。

2.1 Sesision的存储原理

Demo:

public class SessionDemo1 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setCharacterEncoding("UTF=8");
        response.setContentType("text/html;charset=UTF-8");
        // 使用request对象的getSession()获取session,如果session不存在则创建一个
        HttpSession session = request.getSession();
        // 将数据存储到session中
        session.setAttribute("data", "acamy");
        // 获取session的Id
        String sessionId = session.getId();
        // 判断session是不是新创建的
        response.getWriter().println(new Date());
        if (session.isNew()) {
            response.getWriter().print("session创建成功,session的id是:" + sessionId);
        } else {
            response.getWriter().print(
                    "服务器已经存在该session了,session的id是:" + sessionId);
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

效果如下:


可以看到第一次访问时浏览器会创建一个Session对象,给该对象分配一个id,并以JSESSIONID作为键,id作为值以Cookie的形式保存在浏览器中
第二次访问时浏览器发起请求时就会在Cookie里面带着该id信息,服务器就能根据这个id匹配存储在服务器的里面的信息

2.2.浏览器禁用Cookie后的session处理

当我们想实现提供用户浏览过的商品时,利用上面的Cookie和Session思想很容易实现,但是如果浏览器作了上图中的设置,也就是禁用了Cookie,我们又该如何实现呢,这就需要用到URL重写:

用于对sendRedirect方法后的url地址进行重写。
**response.encodeRedirectURL(java.lang.String url) **

//用于对表单action和超链接的url地址进行重写
response.encodeURL(java.lang.String url)

String javax.servlet.http.HttpServletResponse.encodeURL(String url)
Encodes the specified URL by including the session ID in it, or, if encoding is not needed, returns the URL unchanged. The implementation of this method includes the logic to determine whether the session ID needs to be encoded in the URL. For example, if the browser supports cookies, or session tracking is turned off, URL encoding is unnecessary.
看了上面官方对该方法的解释就是对特定的URL添加session ID进行编码,如果不需要的话就不会对URL作任何改变。所有对该方法的实现应该包含判断session ID的信息是否需要添加到URL里面对URL作改变的逻辑。例如,如果浏览器支持cookies,或者session跟踪关了的话,就不对URL作改变。

下面看实例源码:

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();

        // 创建Session
        request.getSession();

        out.write("本网站有如下书:<br/>");
        Set<Map.Entry<String, Book>> set = DB.getAll().entrySet();
        for (Map.Entry<String, Book> me : set) {
            Book book = me.getValue();
            String url = request.getContextPath() + "/buyServlet?id="
                    + book.getId();
            // response. encodeURL(java.lang.String
            // url)用于对表单action和超链接的url地址进行重写,通过此方法带上JSESSSIONID
            url = response.encodeURL(url);// 将超链接的url地址进行重写
            out.println(book.getName() + "   <a href='" + url + "'>购买</a><br/>");
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

/**
 * @author gacl 模拟数据库
 */
class DB {
    private static Map<String, Book> map = new LinkedHashMap<String, Book>();
    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开发"));
    }

    public static Map<String, Book> getAll() {
        return map;
    }
}

class Book {

    private String id;
    private String name;

    public Book() {
        super();
    }

    public Book(String id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    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;
    }
}

BuyServlet:点击购买后的逻辑

public class BuyServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String id = request.getParameter("id");
        Book book = DB.getAll().get(id); // 得到用户想买的书
        HttpSession session = request.getSession();

        HashSet<Book> set = (HashSet) session.getAttribute("set"); // 得到用户用于保存所有书的容器
        if (set == null) {
            set = new HashSet<Book>();
            session.setAttribute("set", set);
        }
        set.add(book);
        // response. encodeRedirectURL(java.lang.String
        // url)用于对sendRedirect方法后的url地址进行重写
        String url = request.getContextPath() + "/listCartServlet";
        url = response.encodeRedirectURL(url);//通过此方法带上JSESSSIONID
        System.out.println(url);
        response.sendRedirect(url);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

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();

        HashSet<Book> set = (HashSet) session.getAttribute("set");
        if (set == null || set.size() == 0) {
            out.write("对不起,您还没有购买任何商品!!");
            return;
        }

        // 显示用户买过的商品
        out.write("您买过如下商品:<br>");
        for (Book book : set) {
            out.write(book.getName() + "<br/>");
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

查看首页源码如下:

每个超链接都被encode加上了JSESSSIONID的信息,所以在访问BuyServlet带着此信息就相当于带着session信息,而BuyServlet在跳转到ListCartServlet也会把url encode一下带上JSESSSIONID,三个Servlet由此实现了session共享。

但当我们刷新列表页时会发现URL里面带的JSESSSIONID已经改变,购物车页面也会清空,所以并不能实现完全没禁用Cookie效果。

当我们开启Cookie时,只有在第一次访问列表页面时会有JSESSSIONID的信息,但我们刷新列表页面时,购物车页面没有清空,页面源码如下所示,没有了JSESSSIONID的信息,证明了encode方法的智能性。

2.3 session对象的销毁时机

session对象默认30分钟没有使用,则服务器会自动销毁session,在web.xml文件中可以手工配置session的失效时间,例如:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name></display-name>
  
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <!-- 设置Session的有效时间:以分钟为单位-->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>

</web-app>

当需要在程序中手动设置Session失效时,可以手工调用session.invalidate方法,摧毁session。

HttpSession session = request.getSession();
//手工调用session.invalidate方法,摧毁session
session.invalidate();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容