Servlet第五篇介绍会话技术、Cookie的API、详解、应用

什么是会话技术基本概念: 指用户开一个浏览器,访问一个网站,只要不关闭该浏览器,不管该用户点击多少个超链接,访问多少资源,直到用户关闭浏览器,整个这个过程我们称为一次会话.为什么我们要使用会话技术?会话跟踪技术可以解决我们很多很多问题。

在论坛登陆的时候,很多时候会有一个小框框问你是否要自动登陆,当你下次登陆的时候就不用输入密码了

根据我以前浏览过的商品,猜我喜欢什么商品

Cookie会话跟踪技术有Cookie和SessionCookie技术是先出现的。我们先讲Cookie技术吧。什么是CookieCookie是由W3C组织提出,最早由netscape社区发展的一种机制

网页之间的交互是通过HTTP协议传输数据的,而Http协议是无状态的协议。无状态的协议是什么意思呢?一旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建立新的连接

服务器无法确认用户的信息,于是乎,W3C就提出了:给每一个用户都发一个通行证,无论谁访问的时候都需要携带通行证,这样服务器就可以从通行证上确认用户的信息。通行证就是Cookie

Cookie的流程:浏览器访问服务器,如果服务器需要记录该用户的状态,就使用response向浏览器发送一个Cookie,浏览器会把Cookie保存起来。当浏览器再次访问服务器的时候,浏览器会把请求的网址连同Cookie一同交给服务器。Cookie API

Cookie类用于创建一个Cookie对象

response接口中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段

request接口中定义了一个getCookies方法,它用于获取客户端提交的Cookie

常用的Cookie方法:

public Cookie(String name,String value)

setValue与getValue方法

setMaxAge与getMaxAge方法

setPath与getPath方法

setDomain与getDomain方法

getName方法

简单使用Cookie

创建Cookie对象,发送Cookie给浏览器、

//设置response的编码

response.setContentType("text/html;charset=UTF-8");

//创建Cookie对象,指定名称和值

Cookiecookie=newCookie("username","zhongfucheng");

//向浏览器给一个Cookie

response.addCookie(cookie);

response.getWriter().write("我已经向浏览器发送了一个Cookie");

浏览器本身没有任何Cookie

访问Servlet1,再回到文件夹中,还是没有发现Cookie,这是为什么呢?我明明向浏览器发送了一个Cookie的

原来发送Cookie给浏览器是需要设置Cookie的时间的。在给浏览器之前,设置一下Cookie的时间

//设置Cookie的时间

cookie.setMaxAge(1000);

再次访问,已经发现文件夹中多了个Cookie的文本了

Cookie细节Cookie不可跨域名性

很多人在初学的时候可能有一个疑问:在访问Servlet的时候浏览器是不是把所有的Cookie都带过去给服务器,会不会修改了别的网站的Cookie**

答案是否定的。Cookie具有不可跨域名性。浏览器判断一个网站是否能操作另一个网站的Cookie的依据是域名。所以一般来说,当我访问baidu的时候,浏览器只会把baidu颁发的Cookie带过去,而不会带上google的Cookie。

Cookie保存中文

上面我们的例子保存的是英文字符,下面我们来看下保存中文字符会怎么样。

response.setContentType("text/html;charset=UTF-8");

PrintWriterprintWriter=response.getWriter();

Stringname="中国";

Cookiecookie=newCookie("country",name);

cookie.setMaxAge(2000);

response.addCookie(cookie);

printWriter.write("我颁发了一个Cookie,值保存的是中文数据");

访问Servlet1,好吧。出异常了!

中文属于Unicode字符,英文数据ASCII字符,中文占4个字符或者3个字符,英文占2个字符。

解决:Cookie使用Unicode字符时需要对Unicode字符进行编码。

//对Unicode字符进行编码

Cookiecookie=newCookie("country",URLEncoder.encode(name,"UTF-8"));

再次访问Servlet1,已经把Cookie成功颁发给浏览器了

我们发现Cookie保存在硬盘的中文数据是经过编码的,那么我们在取出Cookie的时候要对中文数据进行解码

Cookie[]cookies=request.getCookies();

for(inti=;cookies!=null&&i

Stringname=cookies[i].getName();

//经过URLEncoding就要URLDecoding

Stringvalue=URLDecoder.decode(cookies[i].getValue(),"UTF-8");

printWriter.write(name+"------"+value);

}

取出存进Cookie的值

Cookie的有效期Cookie的有效期是通过setMaxAge()来设置的

如果MaxAge为正数,浏览器会把Cookie写到硬盘中,只要还在MaxAge秒之前,登陆网站时该Cookie就有效【不论关闭了浏览器还是电脑】

如果MaxAge为负数,**Cookie是临时性的,仅在本浏览器内有效,关闭浏览器Cookie就失效了,Cookie不会写到硬盘中。Cookie默认值就是-1。这也就为什么在我第一个例子中,如果我没设置Cookie的有效期,在硬盘中就找不到对应的文件。

如果MaxAge为,则表示删除该Cookie。Cookie机制没有提供删除Cookie对应的方法,把MaxAge设置为0等同于删除Cookie

Cookie的修改和删除

上面我们已经知道了Cookie机制没有提供删除Cookie的方法。其实细心点我们可以发现,Cookie机制也没有提供修改Cookie的方法。那么我们怎么修改Cookie的值呢

Cookie存储的方式类似于Map集合,如下图所示

Cookie的名称相同,通过response添加到浏览器中,会覆盖原来的Cookie。以country为名保存的是%E4%B8%AD%E5%9B%BD,下面我再以country为名,把值改变一下

Stringname="看完博客就点赞";

//对Unicode字符进行编码

Cookiecookie=newCookie("country",URLEncoder.encode(name,"UTF-8"));

再次查看Cookie的时候,值已经改变了,但是文件还是那一份

现在我要删除该Cookie,把MaxAge设置为0,并添加到浏览器中即可

Stringname="看完博客就点赞";

//对Unicode字符进行编码

Cookiecookie=newCookie("country",URLEncoder.encode(name,"UTF-8"));

//一定不要忘记添加到浏览器中

cookie.setMaxAge();

response.addCookie(cookie);

printWriter.write("我删除了该Cookie");

访问Servlet,在硬盘已经找不到Cookie的文件了!

注意:删除,修改Cookie时,新建的Cookie除了value、maxAge之外的所有属性都要与原Cookie相同。否则浏览器将视为不同的Cookie,不予覆盖,导致删除修改失败

我们来试验一下把。

Stringname="看完博客就点赞";

//对Unicode字符进行编码

Cookiecookie=newCookie("country",URLEncoder.encode(name,"UTF-8"));

//一定不要忘记添加到浏览器中

cookie.setMaxAge(10000);

response.addCookie(cookie);

上面新建了一个Cookie,我修改下Cookie的其他属性,再删除,看能否把Cookie删除掉

//一定不要忘记添加到浏览器中

cookie.setPath("/ouzicheng");

cookie.setMaxAge();

response.addCookie(cookie);

printWriter.write("删除一个Cookie");

结果Cookie还在硬盘中

Cookie的域名Cookie的domain属性决定运行访问Cookie的域名。domain的值规定为“.域名”Cookie的路径Cookie的path属性决定允许访问Cookie的路径一般地,Cookie发布出来,整个网页的资源都可以使用。现在我只想Servlet1可以获取到Cookie,其他的资源不能获取

使用Servlet2颁发一个Cookie给浏览器,设置路径为"/Servlet1"。

Cookiecookie=newCookie("username","java");

cookie.setPath("/Servlet1");

cookie.setMaxAge(1000);

response.addCookie(cookie);

printWriter.write("该Cookie只有Servlet1获取得到");

使用Servlet3访问服务器,看看浏览器是否把Cookie带上。显然,浏览器访问Servlet3并没有把Cookie带上。

使用Servlet1访问服务器,看看浏览器是否把Cookie带上。答案是肯定的!

Cookie的安全属性

HTTP协议不仅仅是无状态的,而且是不安全的!如果不希望Cookie在非安全协议中传输,可以设置Cookie的secure属性为true,浏览器只会在HTTPS和SSL等安全协议中传输该Cookie

当然了,设置secure属性不会将Cookie的内容加密。如果想要保证安全,最好使用md5算法加密【后面有】。

Cookie的应用显示用户上次访问的时间

其实就是每次登陆的时候,取到Cookie保存的值,再更新下Cookie的值

访问Serlvet有两种情况

第一次访问

已经访问过了

全部代码如下:

SimpleDateFormatsimpleDateFormat=newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");

response.setContentType("text/html;charset=UTF-8");

PrintWriterprintWriter=response.getWriter();

//获取网页上所有的Cookie

Cookie[]cookies=request.getCookies();

//判断Cookie的值是否为空

StringcookieValue=null;

for(inti=;cookies!=null&&i

//获取到以time为名的Cookie

if(cookies[i].getName().equals("time")){

printWriter.write("您上次登陆的时间是:");

cookieValue=cookies[i].getValue();

printWriter.write(cookieValue);

cookies[i].setValue(simpleDateFormat.format(newDate()));

response.addCookie(cookies[i]);

//既然已经找到了就可以break循环了

break;

}

}

//如果Cookie的值是空的,那么就是第一次访问

if(cookieValue==null){

//创建一个Cookie对象,日期为当前时间

Cookiecookie=newCookie("time",simpleDateFormat.format(newDate()));

//设置Cookie的生命期

cookie.setMaxAge(20000);

//response对象回送Cookie给浏览器

response.addCookie(cookie);

printWriter.write("您是第一次登陆啊!");

}

按照正常的逻辑来写,程序流程应该是这样子的。先创建Cookie对象,回送Cookie给浏览器。再遍历Cookie,更新Cookie的值

但是,按照上面的逻辑是做不到的!因为每次访问Servlet的时候都会覆盖原来的Cookie,取到Cookie的值永远都是当前时间,而不是上次保存的时间。

我们换一个逻辑写:先检查(遍历)所有Cookie有没有我要的,如果得不到我想要的Cookie,Cookie的值是null,那么就是第一次登陆,于是就有了上面的代码了。

我们来看下效果吧!当我第一次登陆的时候

Cookie保存在硬盘中。

再次访问Servlet。明显地,取到的就是Cookie的值

显示上次浏览过商品

我就以书籍为例子了!首先设计Book对象

privateStringid;

privateStringname;

privateStringauthor;

publicBook(){

}

publicBook(Stringid,Stringname,Stringauthor){

this.id=id;

this.name=name;

this.author=author;

}

...各种set、get方法

设计一个简单的数据库存储数据。就用LinkedHashMap集合【根据商品的id找书籍所以用Map,删改较多所以用Linked】

privatestaticLinkedHashMaplinkedHashMap=newLinkedHashMap();

//简化开发复杂度,book的id和商品的id相同

static{

linkedHashMap.put("1",newBook("1","javaweb","zhong"));

linkedHashMap.put("2",newBook("2","java","fu"));

linkedHashMap.put("3",newBook("3","oracle","cheng"));

linkedHashMap.put("4",newBook("4","mysql","ou"));

linkedHashMap.put("5",newBook("5","ajax","zi"));

}

//获取到所有书籍

publicstaticLinkedHashMapgetAll(){

returnlinkedHashMap;

}

显示网页上所有的书籍【首页】

printWriter.write("网页上所有的书籍:"+"

");

//拿到数据库所有的书

LinkedHashMaplinkedHashMap=DB.getAll();

Setentry=linkedHashMap.entrySet();

//显示所有的书到网页上

for(Map.EntrystringBookEntry:entry){

Bookbook=stringBookEntry.getValue();

printWriter.write(book.getId()+" "+book.getName()+"

");

}

接着,我们要做的就是给显示的书籍挂上一个超链接,当用户点击想看的书籍时,就跳转到该书籍的详细信息页面

超链接应该把书的id传递过去,不然处理页面是不知道用户想看的是哪一本书的!

//显示所有的书到网页上

for(Map.EntrystringBookEntry:entry){

Bookbook=stringBookEntry.getValue();

printWriter.write("

printWriter.write("

");

}

接收id,找到用户想要看哪一本书,输出该书的详细信息

Stringid=request.getParameter("id");

//由于book的id和商品的id是一致的。获取到用户点击的书

Bookbook=(Book)DB.getAll().get(id);

//输出书的详细信息

printWriter.write("书的编号是:"+book.getId()+"

");

printWriter.write("书的名称是:"+book.getName()+"

");

printWriter.write("书的作者是:"+book.getAuthor()+"

");

点击想要的书籍。

得到书籍的详细信息

既然用户点击了书籍,那么服务器就应该颁发Cookie给浏览器,记住用户点击了该书籍现在问题来了,Cookie的值应该是什么呢?试想一下,待会还要把浏览过的书籍显示出来,所以用书籍的id是最好不过的。想到了用书籍的id作为Cookie的值,我们还要定义一些规则!我们可能有非常多的书籍,不可能把用户浏览过的书籍都显示出来。所以我们定义只能显示3本浏览过的书籍书籍的id都是数字,如果不做任何修改,存到Cookie里边可能就是231,345,123此类的数字,这样取出某一个id的时候就十分费劲并且后面还要判断该书是否存在Cookie里边了,所以我们要把存储到Cookie的书籍id分割起来。所以我们定义”_“作为分隔符按上面的应用,我们的逻辑应该是:先遍历下Cookie,看下有没有我们想要的Cookie。如果找到想要的Cookie,那就取出Cookie的值

StringbookHistory=null;

Cookie[]cookies=request.getCookies();

for(inti=;cookies!=null&&i

if(cookies[i].getName().equals("bookHistory")){

bookHistory=cookies[i].getValue();

}

}

取出了Cookie的值也分几种情况

Cookie的值为null【直接把传入进来的id当做是Cookie的值】

Cookie的值长度有3个了【把排在最后的id去掉,把传进来的id排在最前边】

Cookie的值已经包含有传递进来的id了【把已经包含的id先去掉,再把id排在最前面】

Cookie的值就只有1个或2个,直接把id排在最前边

if(bookHistory==null){

returnid;

}

//如果Cookie的值不是null的,那么就分解Cookie的得到之前的id。

String[]strings=bookHistory.split("\\_");

//为了增删容易并且还要判断id是否存在于该字符串内-----我们使用LinkedList集合装载分解出来的id

Listlist=Arrays.asList(strings);

LinkedListlinkedList=newLinkedList();

linkedList.addAll(list);

if(linkedList.contains(id)){

linkedList.remove(id);

linkedList.addFirst(id);

}else{

if(linkedList.size()>=3){

linkedList.removeLast();

linkedList.addFirst(id);

}else{

linkedList.addFirst(id);

}

}

这么折腾完了,我们的Cookie值就在LinkedList集合里边了。接下来,我们要做的就是把集合中的值取出来,拼接成一个字符串

StringBufferstringBuffer=newStringBuffer();

//遍历LinkedList集合,添加个下划线“_”

for(Strings:linkedList){

stringBuffer.append(s+"_");

}

//最后一个元素后面就不需要下划线了

returnstringBuffer.deleteCharAt(stringBuffer.length()-1).toString();

好的,我们现在已经完成了Cookie值了。接下来设置Cookie的生命周期,回送给浏览器即可

StringbookHistory=makeHistory(request,id);

Cookiecookie=newCookie("bookHistory",bookHistory);

cookie.setMaxAge(30000);

response.addCookie(cookie);

既然我们已经把Cookie回送给浏览器了。那么接下来我们就在首页上获取Cookie的值,显示用户浏览过什么商品就行了

printWriter.write("您曾经浏览过的商品:");

printWriter.write("

");

//显示用户浏览过的商品

Cookie[]cookies=request.getCookies();

for(inti=;cookies!=null&&i

if(cookies[i].getName().equals("bookHistory")){

//获取到的bookHistory是2_3_1之类的

StringbookHistory=cookies[i].getValue();

//拆解成每一个id值

String[]ids=bookHistory.split("\\_");

//得到每一个id值

for(Stringid:ids){

//通过id找到每一本书

Bookbook=linkedHashMap.get(id);

printWriter.write(book.getName());

printWriter.write("

");

}

break;

}

}

好的,我们来试验一下吧!!,第一次访问首页,并没有浏览过的商品

当我点击javaweb书籍再访问首页的时候

再点击ajax然后访问首页

再点击javaweb然后访问首页

点击oracle然后访问首页

好的,经过测试,该程序应该没有什么问题了!

如果文章有错的地方欢迎指正,大家互相交流。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180203G0W5JS00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券