会话只是指一段指定的时间间隔。
会话跟踪是维护用户状态(数据)的一种方式。它也被称为servlet中的会话管理。
Http协议是一个无状态的,所以我们需要使用会话跟踪技术来维护用户状态。 每次用户请求服务器时,服务器将请求视为新请求。 所以需要保持一个用户的状态来识别特定的用户。
HTTP是无状态的,这意味着每个请求被认为是新的请求。如下图所示:
因为需要用于识别特定的用户。
会话跟踪中使用的技术有四种:
Cookie,中文名称为“小型文本文件”或“小甜饼”,指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。
很多网站在你浏览后,会在你电脑中留下小小的档案,也就是我们说的Cookie,以便你再次浏览时,网站会读取它上次给你留下的Cookie资料,如果有的话,就可以根据内容来判断使用者,送出特定的网页内容。
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。所以Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。
Cookie的一个典型的应用是当登录一个网站时,网站往往会请求用户输入用户名和密码,并且用户可以勾选“下次自动登录”。如果勾选了,那么下次访问同一网站时,用户会发现没输入用户名和密码就已经登录了。这正是因为前一次登录时,服务器发送了包含登录凭据(用户名加密码的某种加密形式)的Cookie到用户的硬盘上。第二次登录时,(如果该Cookie尚未到期)浏览器会发送该Cookie,服务器验证凭据,于是不必输入用户名和密码就让用户登录了。
所以:
Cookie是保存在客户端的小文本
保存位置分两种
(1)保存在客户端浏览器所占内存中,关闭浏览器后Cookie也消失
(2)保存在客户端PC机的硬盘上,设置有效时间,超期后失效
但是要注意的是,Cookie既然能把小文本保存在客户端,并在服务器和客户端之间进行传输,那么意味着它也容易造成信息泄露,因此:
不用Cookie保存对保密性要求高的信息,如银行卡密码等
不用Cookie实现某些必要功能,防止Cookie删除后功能出现错误
可以通过浏览器设置阻止Cookie,或手工清除Cookie
Cookie放在请求头Header里,而不是主体Body中,所以GET或POST方式的请求都可以发送Cookie
另外,Cookie还有一些缺陷:
Cookie会被附加在每个HTTP请求中,所以无形中增加了流量
由于在HTTP请求中的Cookie是明文传递的,所以安全性成问题(除非用HTTPS)
Cookie的大小限制在4KB左右。对于复杂的存储需求来说是不够用的
如果你想看到自己的Cookie,也打开某个网站,在控制台调用JS代码:
javascript:alert("Cookie:"+document.cookie)
或 javascript:document.write(document.cookie)
Cookie具有名称,单个值和可选属性,例如注释,路径和域限定符,生存周期和版本号。
许多应用程序希望将用户浏览历史的当前状态存储在客户机中,以便当用户再次返回应用程序时,他从离开的位置开始。通常,对于这个要求,使用cookie。您可以将cookie视为存储在客户机上的键值对数据。当从浏览器中发出请求时,浏览器可以读取或写入这些值。
简单地实例化一个新的Javax.servlet.http.Cookie类创建一个cookie对象。一旦实例化了cookie,就可以设置属性,这将有助于配置cookie。在下面例子中,cookie的setMaxAge()和setHttpOnly()方法被调用,设置cookie的有效期并防止客户端脚本访问。
由于Servlet 3.0 API,将cookie标记为HTTP。这使得cookie可以防止客户端脚本攻击,使cookie更加安全。
默认情况下,每个请求都被视为新的请求。在cookie技术中,servlet响应可以添加cookie。 所以cookie存储在浏览器的缓存中。之后,如果用户发出请求,默认情况下会带上cookie。 因此,我们就可以知道用户旧用户。
Servlet中有2种类型的Cookie
非持久性cookie:它仅适用于单个会话。当用户关闭浏览器时都会删除它。
持久性Cookie:它对多个会话有效。当用户关闭浏览器时也不会删除它。只有在用户注销或注销时才被删除。
维持状态最简单的技术。
Cookie在客户端维护。
如果从浏览器中禁用Cookie,则无法正常工作。只能在Cookie对象中设置文本信息。
javax.servlet.http.Cookie类提供了使用Cookie的功能。
它为Cookie提供了很多有用的方法。
Cookie cookie = new Cookie("sessionId","123456789");
cookie.setHttpOnly(true);
cookie.setMaxAge(-30);
response.addCookie(cookie);
这里的response对象是传给doXXX()方法中的参数。
Cookie ck=new Cookie("user","maxsu jaiswal");//creating cookie object
response.addCookie(ck);//adding cookie in the response
Cookie ck=new Cookie("user","");//deleting value of cookie
ck.setMaxAge(0);//changing the maximum age to 0 seconds
response.addCookie(ck);//adding cookie in the response
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 移除session
request.getSession().removeAttribute("user");
//
Cookie cookie = new Cookie("autologin", "msg");
cookie.setPath(request.getContextPath());
// 将cookie置为过期
/**
* 首先得明白,在服务器端是不能直接删除客户端的cookie。
* 1 它采取的方式是在服务端设置cookie.setMaxAge(0),
* 2 再通过 response.addCookie(cookie) 将这个值返回到客户端后,
* 3 客户端就知道自己的cookie没用了,再删除它。
* 4 故 response.addCookie(cookie); 不能省略,它是一个告知的功能
* 做一下对比理解:
* 好比 商场不能直接扔掉客户的的会员卡(会员卡相当于cookie),
* 它只能设置客户的会员卡失效,
* 客户知道会员卡失效后,自己才把卡扔掉(扔掉才相当于删除cookie的功能)。
*/
cookie.setMaxAge(0);
response.addCookie(cookie);
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
再次强调:
public void deleteLoginCookies(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
// 遍历浏览器发送到服务器端的所有Cookie,找到自己设置的Cookie
for (Cookie cookie : cookies) {
String cookieName = cookie.getName();
if (cookieName.equals(loginIdCookieKey)) {
// 设置Cookie立即失效
cookie.setMaxAge(0);
/**
* 删除Cookie时,只设置maxAge=0将不能够从浏览器中删除cookie,
* 因为一个Cookie应当属于一个path与domain,所以删除时,Cookie的这两个属性也必须设置。
*
* 误区:刚开始时,我没有发现客户端发送到服务器端的cookie的path与domain值为空这个问题。
* 因为在登陆系统时,我设置了Cookie的path与domain属性的值,就误认为每次客户端请求时,都会把Cookie的
* 这两个属性也提交到服务器端,但系统并没有把path与domain提交到服务器端(提交过来的只有Cookie的key,value值)。
*/
// 重点是这里1,必须设置domain属性的值
cookie.setDomain(domain);
// 重点是这里2,必须设置path属性的值
cookie.setPath(path);
response.addCookie(cookie);
}
}
}
}
Cookie ck[]=request.getCookies();
for(int i=0;i<ck.length;i++){
out.print("
"+ck[i].getName()+" "+ck[i].getValue());//printing name and value of cookie
}
示例使用以下代码:
Cookie[] cookies = request.getCookies();
for(Cookie cookie : cookies)
{
//cookie.getName();
//cookie.getValue()
}
mylogin.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>登陆页面</title>
</head>
<body>
<%
String username = null;
String password = null;
//获取请求中的所有cookie对象
Cookie[] cookies = request.getCookies();
//只要存在Cookie,就查找是否有用户名和密码
if(cookies != null) {
for(Cookie cookiesTemp : cookies) {
if(cookiesTemp.getName().equals("username")) {
username = cookiesTemp.getValue();
}
if(cookiesTemp.getName().equals("password")) {
password = cookiesTemp.getValue();
}
}
}
if(username != null && password != null) {
request.getRequestDispatcher("LoginServlet?username=" + username + "&pwd=" + password).forward(request, response);
}
%>
<form name="form1" action="LoginServlet" method="post">
用户名:<input type="text" id="username" name="username"><br>
密码:<input type="password" id="pwd" name="pwd"><br>
<select id="timelength" name="timelength">
<option value="0" selected>每次都需要登陆</option>
<option value="3">3天内免登陆</option>
<option value="7">7天内免登陆</option>
</select><br>
<input type="submit" name="submit" value="登陆">
</form>
</body>
</html>
myhome.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录成功-主页显示</title>
</head>
<body>
<div>
您已经成功登录 ,你的用户名是:${requestScope.username }<br>
您的密码是:${requestScope.password }
</div>
</body>
</html>
CookieServlet.java
package cn.edu.cuit.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;
@WebServlet("/LoginServlet")
public class CookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置输出格式和编码
resp.setContentType("text/html;charset=utf-8");
// 获取参数
String username = req.getParameter("username");
String password = req.getParameter("pwd");
String timeLength = req.getParameter("timelength");
int days = 0;
// 处理cookie保持天数
if (timeLength != null) {
days = Integer.parseInt(timeLength);
}
// 设置cookie
if (days != 0) {
Cookie usernameCookie = new Cookie("username",username);
Cookie passwordCookie = new Cookie("password",password);
usernameCookie.setMaxAge(days*24*3600);
passwordCookie.setMaxAge(days*24*3600);
resp.addCookie(usernameCookie);
resp.addCookie(passwordCookie);
}
// 登录判断
if (!username.equals("") && !password.equals("")) {
req.setAttribute("username", username);
req.setAttribute("password", password);
}
// 跳转
req.getRequestDispatcher("myhome.jsp").forward(req, resp);
}
}
输入用户名和密码:fhzheng,123456,不为空,会跳转到成功页
这时,可以再查看硬盘中的cookie,低版本的IE是可以直接用文本查看工具打开看到Cookie的。高版本,在F12中,用开发者工具就可以查看。
为了方便展示,我们用IE浏览器登陆一次,然后可以在浏览器设置中,找到存放Cookie文件的位置:
可以看到,如果有用过IE浏览器,有平时登陆其他网站留下的Cookie:
看具体的Cookie的数据
初学时,可以这么认为:Cookie是个小文本,而且也基本是按照key/value的方式存储,所以也才有了封装的Cookie对象用来保存一对键值对。因为Cookie实现自动登录涉及到用户信息安全的问题,实际上真正的自动登录不会像上面这么简单粗暴的,从我们查看Cookie就可以知道,账号密码都可以赤裸裸地看到,这显然是不行的。
cookie就是服务器放在用户机器【即客户端】上的一小块信息【一种有结构的数据】。cookie以前用来保存登录信息,这样用户就不用每次从同一台机器访问页面都要再次输入登录信息。cookie是第一个js可以利用的客户端-服务端的通信手段【方便了前端的开发,提高了用户体验】,每次客户端向服务器发送请求时,为这个服务器存储的cookie会与其他信息一起放入http包中发送到服务器。
使用document.cookie可以获取给定页面可以访问的所有cookie组成的字符串;
每个cookie之间用分号间隔
创建一个cookie就是创建一个符合格式的字符串:
cookie_name=cookie_value;
expires = expiration_time;
path = domain_path;
domain = domain_name;
其中名称和值时必须的。将这个字符串赋值给document.cookie就创建了一个cookie。
自动填写用户登录信息的实现原理
实现原理:
浏览器将表单数据封装在HTTP请求发送给服务器,服务器接收到请求后先检查”记住我“复选框有没有勾选,若勾选,则设置cookie其实就是创建字符串,往字符串添加用户的名字和邮件地址等信息。服务器在返回响应时,会将设置的cookie封装在HTTP响应中发送给浏览器。浏览器在接收到响应后,先查看是否有cookie信息,若有,则将服务器返回的cookie信息存储起来。
当用户再一次从同一台机器上访问同一个页面时,服务器会将请求页面HTML文件发送给浏览器,浏览器渲染此HTML页面时会执行页面中的JS代码。页面中的JS代码会先检查用户名和邮件地址是否已经存储在cookie中,若是,则执行从本地存储读取cookie的操作(利用document.cookie获取),然后从cookie字符串中提取需要的字符串即用户名和邮件地址等,最后将提取上来的字符串填入表单对应的字段中。这样,用户就不用再输入登录信息了。
必须指定input控件的type属性为:type=“hidden”, uname是隐藏的字段名称,maxsu是隐藏字段uname的值。
<input type="hidden" name="uname" value="maxsu">
它广泛用于网站的评论形式。 在这种情况下,我们将页面ID或页面名称存储在隐藏字段中,以便可以唯一标识每个页面。
Cookie是否被禁用隐藏表单字段都会始终有效。
它在服务器端维护。每个页面需要额外提交表单和字段。只能使用文本信息。
在URL重写中,我们将令牌或标识符附加到下一个Servlet或下一个资源的URL。可以使用以下格式发送参数名称/值对,用=符号分隔名称和值,使用&符号将参数名/值对与其他参数分开。当用户单击超链接时,参数名称/值对将被传递到服务器。 从Servlet中可以使用getParameter()方法获取指定参数的值。
无论Cookie是否被禁用(浏览器无关),它将始终有效。
每个页页不需要额外的表单提交。
它只能在链接上工作。
它只能发送文本信息。
除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态,即Session是服务器端使用的一种保存客户端状态的机制。Cookie在客户端,Session在服务器端。
(1)Session会话
我们通常都会把Session翻译成会话,因此我们可以把客户端浏览器与服务器之间一系列交互的动作称为一个 Session。即客户端向服务器发送请求,服务器接收请求并生成响应返回给客户端,这样一次连续的调用过程。
从这个语义出发,就涉及到所谓Session持续的时间,以及Session过程中发生了什么操作等。
(2)Session作用域
其次,Session指的是服务器端为客户端所开辟的存储空间,在其中保存的信息就是用于保持状态。
从这个语义出发,则涉及到Session中存放什么内容,如何根据键值从Session中获取匹配的内容等。
(Session的实现机制,以及和Cookie的区别,存在的价值,本篇暂不展开,会在后面的文章中单独拿出来解释)
Servlet中的HttpSession对象的使用,在应用容器中,它为每个用户创建会话ID。
容器使用此标识来识别特定的用户。
HttpSession的一个对象可用于执行两个任务:
HttpServletRequest接口提供了两种获取HttpSession对象的方法:
public HttpSession getSession():返回与此请求相关联的当前会话,或者如果请求没有会话,则创建一个会话。
public HttpSession getSession(boolean create): 返回与此请求相关联的当前HttpSession,如果没有当前会话,并且create的值为true,则返回一个新会话。
public String getId() - 返回一个包含唯一标识符值的字符串。
public long getCreationTime() - 返回创建此会话的时间,以1970年1月1日GMT格林尼治时间以来的毫秒为单位。
public long getLastAccessedTime() - 返回客户端发送与此会话相关联的请求的最后一次,为1970年1月1日GMT以来的毫秒数。
public void invalidate() - 使此会话无效,然后取消绑定绑定到该对象的任何对象。
要在会话范围内设置属性,可使用HttpSession接口的setAttribute()方法设置属性,并使用getAttribute()方法获取属性。
HttpSession中定义了三个与属性相关的方法,get / set / remove Attribute,分别用来 获取 / 设置 / 删除 属性。
这三个方法的声明和 HttpServletRequest 中与属性相关的方法是相同的,主要区别在于:
有效范围不同
请求中的属性只在当前请求内有效,只有通过转发才能把当前请求对象转发到下个资源
会话属性在会话对象中有效,即客户端和服务器连接后,只要没有关闭浏览器,服务器也正常,则在该次会话中属性一直有效
另,由于会话对象有效时间长,安全性相对较低,所占资源较多。所以,请求属性能解决的问题,就用请求对象,必须时才使用会话。
<session-config>
<session-timeout>50</session-timeout>
</session-config>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.setMaxInactiveInterval(2*60*60);
}
HttpSession接口提供了setMaxInactiveInterval方法用以设定session的有效时间,以秒为单位,若形参为负数,则表示永不失效。
如果用以上两种方式同时设置Session的有效时间,则以设定的较小的时间为准。
需要注意的是,上面这两种方式还是有区别的:
除了根据有效时间使会话失效之外,还可以调用HttpSession中的invalidate方法,使会话立即失效。
我们在涉及账户登录后常常用到session保存部分用户信息,在用户选择登出时,就要使用invalidate让session失效,以保证用户信息安全,同时提高服务器的效率。
根据早期的HTTP协议,每次request-reponse时,都要重新建立TCP连接。TCP连接每次都重新建立,所以服务器无法知道上次请求和本次请求是否来自于同一个客户端。因此,HTTP通信是无状态的。服务器认为每次请求都是一个全新的请求,无论该请求是否来自同一地址。
但是这也带来了问题,假如不使用Session或Cookie,那么就意味着假如你登录了某个购物网站,你的每次请求因为无状态,购物网站的服务器都无法判断你的身份和登陆与否,意味着为了保持登陆你必须浏览某个商品时登陆一次,浏览另一个商品又要登陆一次。
这种用户体验谁还愿意上购物网站?于是出现了Cookie,它把少量的用户信息存储在用户自己电脑上,因为它在同一个域名下是全局的,所以用户访问时,服务器就可以从该域名下任意页面读取Cookie中的信息,以判断登陆状态。
但Cookie存在用户端,存储尺寸也有大小限制,用户自身还可以禁用,甚至可见并修改,安全性极差。为了安全,又能方便地读取全局信息,于是出现了新的存储会话机制,Session。
有了Session,就能让用户在一次会话中的多次HTTP请求产生关联,让多个页面都能读取到Session域里面的值。Session信心存放在服务器端,也很好地解决了安全问题。
但是同样有疑问,若干客户端和服务器连接,服务器会为每个客户端的一次会话创建一个会话对象Session,如何区分哪个Session对应的是哪个客户端呢?
答案是,多数容器是采用Cookie机制来实现Session机制,也就是说,利用Cookie来保存 “客户端” 和 “服务器里会话对象” 之间的对应关系。
使用Cookie实现会话机制的过程是:
当容器创建一个新的HttpSession对象后,会生成一个随机数,称之为会话ID,并将ID值封装成一个名为JSESSIONID的Cookie,返回给客户端;
之后的请求,在调用request.getSession方法获得会话对象时,容器会先从request中获取JSESSIONID的值,根据值查找到对应的会话对象,返回使用;
如果没有获得JSESSIONID值,则容器认为当前请求没有相关联的会话对象,会重复第一步进行生成。
去餐馆点了牛排,得到号码牌(JSESSIONID)
你走开了几步之后,服务员就忘了你是谁
你如果想取你的牛排,你就需要这个号码牌去找服务员领
服务员能根据号码牌确认你是顾客,你点过餐,把你对应的牛排拿给你
Cookie在浏览器设置是可以被阻止的,那么根据其Session的实现机制,如果Cookie被禁用,那么Session也会受到影响。
Cookie被阻止,那么根据其实现机制,则找不到JSESSIONID的Cookie,会认为是首次登陆,所以无法利用Session保持用户的登陆状态。
解决办法:强制把JSESSIONID传递给相关资源
Java Servlet API 中提出了跟踪Session的另一种机制,如果客户端浏览器不支持Cookie,容器可以重写客户请求的URL,把JSESSIONID添加到 URL信息中。HttpServletResponse接口提供了重写 URL 的方法:public String encodeURL(String url)
先判断当前的Web组件是否启用Session,如果没有启用Session,直接返回参数url
如果启用Session,再判断客户端浏览器是否支持Cookie
如果支持Cookie,直接返回参数url
如果不支持Cookie,就在参数url中加入JSESSIONID信息,然后返回修改后的url
- 都是保存用户信息,不同在于Session存储在服务器端,Cookie是存储在客户端
- Session中可以保存任意对象,Cookie只能保存字符串
- Session随会话结束而关闭,Cookie可以长期保存在客户端硬盘上,也可以临时保存在浏览器内存中
- Session用来保存重要信息,Cookie用来保存不重要的用户信息