Cookies
用户信息:
Http 是一个无状态协议,就是这一次请求和上一次请求是没有任何关系的,互不认识的,没有关联的(无论是客户端还是服务器都不记录http的相关信息)。
这样设计一方面减轻了服务器端的负载,另一方面减小了http请求的开销。
但是针对某些特殊的场景,需要时刻记录用户的相关信息,这该如何处理呢?
cookie:
Http 是一个无状态的协议,但访问某些资源的时候,往往需要经过认证的账户才能访问,而且要一直保持在线状态,
所以,cookie是一种在浏览器端解决的方案,将登陆认证之后的用户信息保存在本地浏览器中,
后面每次发起http请求,都自动携带上该信息,就能达到认证用户,保持用户在线的作用。
定义:
Cookies 是存储在客户端计算机上的文本文件,并保留了用户的各种跟踪信息 。
作用:
会话保持,如完成用户的登录与状态保持。
设置cookie的方法:
在 Http 的 Response 报头中可以携带 Set-Cookie 字段来完成。
cookie工作原理
客户端向服务器发起登录请求,
服务器在http响应头中添加Set-Cookie信息,其值的格式通常是name = value的格式。例如:姓名、年龄或识别号码等。
浏览器收到响应后会根据头中的字段保存cookie(存储在本地计算机上),
当下一次浏览器向 Web 服务器发送任何请求时,
浏览器会把这些 Cookies 信息发送到服务器,服务器将使用这些信息来识别用户。
cookie的内容主要包括:名字,值,过期时间,路径和域。
cookie构成
Cookies 通常设置在 HTTP 头信息中。
设置 Cookie 的http请求,向 Servlet 会发送如下的头信息:
HTTP/1.1 200 OK
Date: Fri, 04 Feb 2000 21:03:38 GMT
Server: Apache/1.3.9
Set-Cookie: name=xyz; expires=Friday, 04-Feb-07 22:03:38 GMT; path=/; domain=bit.com
Connection: close
Content-Type: text/html
Set-Cookie 头包含了一个名称值对、一个 GMT 日期、一个路径和一个域。名称和值会被 URL 编码。
expires 字段是一个指令,告诉浏览器在给定的时间和日期之后过期("忘记")该 Cookie。
如果浏览器被配置为存储 Cookies,它将会保留此信息直到到期日期。
如果用户的浏览器指向任何匹配该 Cookie 的路径和域的页面,它会重新发送 Cookie 到服务器。
浏览器的头信息可能如下所示:
GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc) Host: zink.demon.co.uk:1126
Accept: image/gif, */*
Accept-Encoding: gzip Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Cookie: name=xyz
Servlet 就能够通过请求方法 request.getCookies() 访问 Cookie,该方法将返回一个 Cookie 对象的数组。
Servlet 操作cookie方法
方法 | 描述 |
---|---|
public void setDomain(String pattern) | 该方法设置 cookie 适用的域,例:w3cschool.cn |
public String getDomain() | 该方法获取 cookie 适用的域,例:w3cschool.cn |
public void setMaxAge(int expiry) | 该方法设置 cookie 过期的时间(以秒为单位)。如果不这样设置,cookie 只会在当前 session 会话中持续有效。 |
public int getMaxAge() | 该方法返回 cookie 的最大生存周期(以秒为单位),默认情况下,-1 表示cookie 将持续下去,直到浏览器关闭。 |
public String getName() | 该方法返回 cookie 的名称。名称在创建后不能改变。 |
public void setValue(String newValue) | 该方法设置与 cookie 关联的值。 |
public String getValue() | 该方法获取与 cookie 关联的值。 |
public void setPath(String uri) | 该方法设置 cookie 适用的路径。如果您不指定路径,与当前页面相同目录下的(包括子目录下的)所有 URL 都会返回 cookie。 |
public String getPath() | 该方法获取 cookie 适用的路径。 |
示例1:提交表单,设置 cookie:
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
// 为名字和姓氏创建 Cookies
Cookie firstName = new Cookie("first_name",request.getParameter("first_name"));
Cookie lastName = new Cookie("last_name",request.getParameter("last_name"));
// 为两个 Cookies 设置过期日期为 24 小时后
firstName.setMaxAge(60*60*24);
lastName.setMaxAge(60*60*24);
// 在响应头中添加两个 Cookies
response.addCookie( firstName );
response.addCookie( lastName );
// 设置响应内容类型
response.setContentType("text/html");
// 设置响应的编码格式
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
String title = "设置 Cookies 实例";
String docType = "<!doctype html public>\n";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + title + "</h1>\n" +
"<ul>\n" +
" <li><b>名字</b>:"
+ request.getParameter("first_name") + "\n" +
" <li><b>姓氏</b>:"
+ request.getParameter("last_name") + "\n" +
"</ul>\n" +
"</body></html>");
}
public void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
doGet(request,response);
}
}
代码运行结果:
示例2:获取写入的 cookie
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
Cookie cookie = null;
Cookie[] cookies = null;
// 获取与该域相关的 Cookies 的数组
cookies = request.getCookies();
// 设置响应内容类型
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
String title = "Reading Cookies Example";
String docType = "<!doctype html>\n";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body>\n" );
if( cookies != null ){
out.println("<h2>查找 Cookies 名称和值</h2>");
for (int i = 0; i < cookies.length; i++){
cookie = cookies[i];
out.print("名称:" + cookie.getName( ) + ",");
out.print("值:" + cookie.getValue( )+" <br/>");
}
}
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
doGet(request,response);
}
}
示例3:删除 cookie
- 读取一个现有的 cookie,并把它存储在 Cookie 对象中。
- 使用 setMaxAge() 方法设置 cookie 的年龄为零,来删除现有的 cookie。
- 把这个 cookie 添加到响应头。
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
// 删除 cookie
Cookie cookie = null;
Cookie[] cookies = null;
// 获取与该域相关的 Cookies 的数组
cookies = request.getCookies();
// 设置响应内容类型
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
String title = "Delete Cookies Example";
String docType =
"<!doctype html>\n";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body>\n" );
if( cookies != null ){
out.println("<h2>Cookies 名称和值</h2>");
for (int i = 0; i < cookies.length; i++){
cookie = cookies[i];
if((cookie.getName( )).compareTo("first_name") == 0 ) {
cookie.setMaxAge(0); //将过期时间设置为0,删除指定cookie
response.addCookie(cookie);
out.print("已删除的 cookie:" +
cookie.getName( ) + "<br/>");
}
out.print("名称:" + cookie.getName( ) + ",");
out.print("值:" + cookie.getValue( )+" <br/>");
}
}
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
doGet(request,response);
}
}
session
session:
将用户敏感信息放到本地浏览器中,能解决一定的问题,但是又引进了新的安全问题,
一旦cookie丢失,用户信息泄露,就容易造成跨站攻击。
解决方法:将用户敏感信息保存至服务器,而服务器本身采用 md5 算法或相关算法生成唯一值(session id),将该值保存值客户端浏览器,之后,客户端的后续请求,浏览器都会自动携带该id,进而再在服务器端认证,进而达到状态保持的效果。
定义:
session 是存储在服务器上的文本文件,并保留了用户的各种跟踪信息。
作用:
会话保持,如完成用户的登录与状态保持,因为在服务器端,所以相对安全一些。
session工作原理
当程序需要为某个客户端的请求创建一个session的时候,
服务器首先检查这个客户端的请求里是否已包含了一个session标识称为session id,
如果已包含一个session id,则说明以前已经为此客户端创建过session,
服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个);
如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,
session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,
这个session id将被在本次响应中返回给客户端保存。
保存这个session id的方式可以采用cookie,
这样浏览器下次发请求的时候,这个session id会被放置在请求头中,和cookie一起发送回来。
服务器再通过内存中保存的session id跟cookie中保存的ssession id进行比较,
并根据id在内存中找到之前创建的session对象,提供给请求使用,
也就是服务器会通过session保存一个状态记录,浏览器会通过cookie保存状态记录,
服务器通过两者的对比实现跟踪状态,这样极大的避免了cookie被篡改而带来的安全性问题。
Servlet 操作session方法
HttpSession 对象:
- Servlet 提供了 HttpSession 接口,该接口提供了一种跨多个页面请求或访问网站时识别用户以及存储有关用户信息的方式。
- Servlet 容器使用 HttpSession 接口来创建一个 HTTP 客户端和 HTTP 服务器之间的 session 会话。会话持续一个指定的时间段,跨多个连接或页面请求。
- 通过调用 HttpServletRequest 的公共方法 getSession() 来获取 HttpSession 对象:HttpSession session = request.getSession();
方法 | 描述 |
---|---|
public Object getAttribute(String name) | 该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null。 |
public Enumeration getAttributeNames() | 该方法返回 String 对象的枚举,String 对象包含所有绑定到该 session 会话的对象的名称。 |
public long getCreationTime() | 该方法返回该 session 会话被创建的时间,自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。 |
public String getId() | 该方法返回一个包含分配给该 session 会话的唯一标识符的字符串。 |
public long getLastAccessedTime() | 该方法返回客户端最后一次发送与该 session 会话相关的请求的时间。 |
public int getMaxInactiveInterval() | 该方法返回 Servlet 容器在客户端访问时保持 session 会话打开的最大时 间间隔,以秒为单位。 |
public void invalidate() | 该方法指示该 session 会话无效,并解除绑定到它上面的任何对象。 |
public boolean isNew() | 如果客户端还不知道该 session 会话,或者如果客户选择不参入该 session 会话,则该方法返回 true。 |
public void removeAttribute(String name) | 该方法将从该 session 会话移除指定名称的对象。 |
public void setAttribute(String name, Object value) | 该方法使用指定的名称绑定一个对象到该 session 会话。 |
public void setMaxInactiveInterval(int interval) | 该方法在 Servlet 容器指示该 session 会话无效之前,指定客户端请求之间的时间,以秒为单位。 |
示例1:
如何使用 HttpSession 对象获取 session 会话创建时间和最后访问时间。
如果不存在 session 会话, 我们将通过请求创建一个新的 session 会话。
public class HelloServlet extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 如果不存在 session 会话,则创建一个 session 对象
HttpSession session = request.getSession(true);
// 获取 session 创建时间
Date createTime = new Date(session.getCreationTime());
// 获取该网页的最后一次访问时间
Date lastAccessTime = new Date(session.getLastAccessedTime());
String title = "欢迎回来";
String visitCountKey = new String("visitCount");
Integer visitCount = new Integer(0);
String userIDKey = new String("userID");
String userID = new String("abcd");
// 检查网页上是否是新的访问者
if(session.isNew()){
title = "欢迎来到我的网站";
session.setAttribute(userIDKey,userID);
}
else{
visitCount = (Integer) session.getAttribute(visitCountKey);
visitCount++;
userID = (String)session.getAttribute(userIDKey);
}
session.setAttribute(visitCountKey,visitCount);
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
String docType = "<!doctype html>\n";
out.println(docType + "<html>\n" +
"<head><title>" +title+ "</title></head>\n" +
"<body>\n" +
"<h1>" + title + "</h1>\n"+
"<h2> Session 信息 </h2>" +
"<p>" +
"ID: " + session.getId() + "<hr/><br/>"+ "Create Time: " + createTime + "<hr/><br/>"+
"Time of Last Access: " + lastAccessTime + "<hr/><br/>"+
"User ID: " + userID + "<hr/><br/>"+
"Number of visits: " + visitCount + "<hr/><br/>"+
"</p>"+ "</body>" +
"</html>");
}
}
代码运行结果:
step1:发起请求,查看结果
step2:分析请求结果
得到响应时,服务器会自动向浏览器设置cookie,将session id设置进浏览器
step3:多次刷新
每次请求,浏览器request都会自动携带session id,以供服务器器来进行标识
session 持久化
一般情况下,session是直接存储在指定服务器的内存中的,
一般使用够了,但是在集群场景当中,可能需要通过 session共享,来保证用户在不同的服务器上都可以得到认证。
所以一般将用户的session信息统一保存在后端的数据库服务器中,从而达到不同服务器间session的共享。
即:将session保存在服务器数据库或者文件中的行为称之为session持久化
URL 重写
由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。
URL重写:把session id直接附加在URL路径的后面。
附加方式:
- 作为URL路径的附加信息,表现形式为:http://…/xxx;jsessionid=ByOK … 99zWpBng!-145788764
- 作为查询字符串附加在URL后面,表现形式为:http://…/xxx?jsessionid=ByOK … 99zWpBng!-145788764
- 这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。
Http Server + session&cookie
运行结果:
浏览器上查看 Cookie:
Fiddler 抓包 查看 Set-Cookie:
再次访问:
抓包,查看Cookie:
cookie和session的区别
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
- cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
- 所以个人建议:
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中