JavaWeb之Session:
一、Session的概述
- 当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含一个session id。
- 如果有则表明已经为此客户端创建过session,服务器就按照这个session id查找出服务器端保存的session(查找失败的话,会创建一个新的session)。
- 如果请求中不包含session id,则就回创建一个新的session,并把session id返回到客户端进行保存。客户端浏览器可以通过cookie保存session id,但是cookie可以被浏览器禁止。
- 禁用Cookie后可以有两种方式实现session的处理。1.url重写的技术,就是把session id直接附加在URL路径的后面。2.还有一种表单隐藏字段技术,就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。
- session id一旦被攻击者而已获得,攻击者可以伪装成请求者对服务器发起请求。
- session的销毁
1、超时(一般服务器设置超时时间为30分钟)服务器会销毁session
2、点击控制台的红色按钮异常关闭服务器要销毁session
3、手动调用session的invalidate方法session.invalidate();
二、Session和Cookie的区别
在学校旁边的一家咖啡厅,有消费十杯碗免费赠送一杯的活动。然而一次性消费十杯的可能性很小,需要用某种方式来记录顾客的消费状态,这时就有两种方案:
- cookie方案: 发给顾客一张卡,上面记录着消费量,一般还有个时限。每次消费的时候顾客只要出示这张卡,则此次消费的状态就被记录下来了。这就是在客户端保持状态。
- 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,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID
过程:
第一次访问: 当用浏览器登录到某网站服务器时,先找对应的Cookie文件,当首次访问是当然没有Cookie文件,所以在请求头部中没有Cookie的内容,即在请求头部中没有类似Cookie:
JSESSIONID=XXXXXXXXXXXXXXX的内容,这时当请求到达服务器后,服务器看请求头中没有JSESSIONID值,于是生成一个Session对象,并由某种算法产生一个值赋给这个Session的id,并将SessionId,和Session对象放入HashMap中,然后将这个SessionId发回以客户端,即在响应的头部有一行:Set-Cookie:JSESSIONID=XXXXXXXXXXXXXX,浏览器解析后会将在JSESSIONID和值记录到Cookie中。
再次访问: 当用浏览器再次请求此网站的另一页面时,浏览器检查看有没有Cookie,这时有Cookie(前提是Cookie没有过期),会自动的把Cookie的内容附加到请求头中,即在请求头附加了一行:Cookie:
JSESSIONID=XXXXXXXXXXXXXXXXXXXXXXX,服务器接收到请求后会根据JSESSIONID的值知道SessionId的值,在tomcat中两个值是一样的,然后你可根据这个SessionId找到对应的Session,可由Session中登录后设定的某些值是否为空,来判断此用户是否登录。
四、Session的常用API介绍
方法 | 作用 |
---|---|
public void setAttribute(String name, Object value) | 使用指定名称将对象绑定到此会话。如果具有同样名称的对象已经绑定到该会话,则替换该对象。 |
public Object getAttribute(String name) | 返回与此会话中的指定名称绑定在一起的对象,如果没有对象绑定在该名称下,则返回 null。 |
public String getId() | 返回包含分配给此会话的唯一标识符的字符串。标识符是由 servlet 容器分配的,并且是与实现相关的。 |
public java.util.Enumeration getAttributeNames() | 返回包含绑定到此会话的所有对象的名称的 String 对象的 Enumeration。 |
public boolean isNew() | 如果客户端还不知道该会话,或者客户端选择不加入该会话,则返回 true。 |
public void setMaxInactiveInterval(int interval) | 指定在 servlet 容器使此会话失效之前客户端请求之间的时间间隔,以秒为单位。负数时间指示会话永远不会超时。 |
public int getMaxInactiveInterval() | 返回 servlet 容器在客户端访问之间将使此会话保持打开状态的最大时间间隔,以秒为单位。在此间隔之后,servlet 容器将使会话无效。 |
public void invalidate() | 使此会话无效,然后取消对任何绑定到它的对象的绑定。 |
public HttpSession getSession()
五、Session的应用案例
1、session的设置保存和取值:
SaveSession.java对session对象进行存值
public class SaveSession extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD hh:mm:ss");
// 第一次访问request.getSession()。有session得到session,没有session就创建并得到session
HttpSession session = request.getSession();
session.setAttribute("date", new Date());
response.getWriter().write("save session ok!");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
GetSession.java对session对象中的值进行获取:
public class GetSession extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
HttpSession session = request.getSession();
// 使用Cookie的技术,将JSESSION,session的id值持久化保存到Cookie中
String id = session.getId();
//将JSESSIONID的值持久化保存到Cookie中
Cookie cookie = new Cookie("JSESSIONID", id);
cookie.setMaxAge(24 * 60 * 60 * 7);
cookie.setPath("/");
response.addCookie(cookie);
Date date = (Date) session.getAttribute("date");
response.getWriter().write(sdf.format(date));
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2、使用Session实现技术实现购物车:
有三个jsp页面组成显示操作,分别是:
productsCart.jsp、productsList.jsp和cleanCart.jsp;
一个CartSession.java类组成:
分析:
1、在productsList.jsp页面显示商品的列表,在商品链接的后添加id号
2、在CartSession.java获取并创建一个Session对象(名为cart,值为一个Map集合key为id号,value记录商品添加的数量),并获得商品的id值,
3、通过判断sessio对象cart是否存在和某商品是否存在购物车中改变cart的状态和商品的数量
4、在productsCart.jsp页面展示出购物车中的信息
5、cleanCart.jsp清除购物车信息
productsList.jsp页面显示商品的列表
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>商品列表</title>
</head>
<body >
<h1>商品列表:</h1>
<table border="1">
<tr>
<th>商品序号</th>
<th>商品名称</th>
<th>商品描述</th>
</tr>
<tr>
<td>1</td>
<td>西游记</td>
<td><a href="/day06/cart?id=1"> 添加到购物车</a></td>
</tr>
<tr>
<td>2</td>
<td>水浒传</td>
<td><a href="/day06/cart?id=2"> 添加到购物车</a></td>
</tr>
<tr>
<td>3</td>
<td>红楼梦</td>
<td><a href="/day06/cart?id=3"> 添加到购物车</a></td>
</tr>
<tr>
<td>4</td>
<td>三国演义</td>
<td><a href="/day06/cart?id=4"> 添加到购物车</a></td>
</tr>
</table>
<br>
<hr>
<a href="/day06/Cart/productsCart.jsp">查看购物车</a>
<a href="/day06/Cart/cleanCart.jsp">清空购物车</a>
</body>
</html>
CartSession.java处理具体的业务逻辑
package cn.syj.Servlet.Session;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class CartSession extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
// 1、取得session对象
HttpSession session = request.getSession();
String id = request.getParameter("id");
// 2、得到session名称为cart的session对象的值,使用一个Map集合,进行存储商品id和商品数量count
Map<String, Integer> cart = (Map<String, Integer>) session.getAttribute("cart");
// 3、判断取得的cart值是否为空,
if (cart == null) {
// 3.1如果为空就说明还没有对cart进行任何操作(第一次点击添加购物车的操作),将次id添加到Map集合中
// 第一次添加数量肯定为 1
//用来存放购物车的id和数量count
cart = new HashMap<String, Integer>();
cart.put(id, 1);
} else {
// 3.2如果不为 null ,说明在此之前已经有商品已经在购物车,只不过后续再添加的商品不知道是不是存在(再次进行判断)
// 当再次添加的商品的值为null的时候,说明此商品以前没有被添加过
Integer count = cart.get(id);
if (count == null) {
// 将第一次添加到购物车的商品置为1
count = 1;
} else {
//以前添加过将商品数量加1
count += 1;
}
cart.put(id, count);
}
//4.将session名称(第一个cart)为cart的值为cart(第二个为cart)的session重新设置一下
session.setAttribute("cart", cart);
response.getWriter().write("添加购物车成功,<a href='/day06/Cart/productsList.jsp'>继续购物</a>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
productsCart.jsp购物车信息:
<%@page import="java.util.Set"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>购物车商品</title>
</head>
<body>
<%
String[] products = { "西游记", "水浒传", "红楼梦", "三国演义" };
Map<String, Integer> cart = (Map<String, Integer>) session.getAttribute("cart");
//如果为空,说明没有任何商品添加在购物车
if (cart == null) {
%>
购物车暂无商品
<%
} else {
//如果不为空,说明购物车中有商品,cart中有值
//取到cart的key集合
Set<String> keys = cart.keySet();
//遍历集合
for (String key : keys) {
%>
商品名称:<%=products[Integer.parseInt(key) - 1]%>
商品数量:<%=cart.get(key) %><br>
<%
}
}
%>
<hr>
<a href="/day06/Cart/productsList.jsp">返回购物列表</a>
</body>
</html>
cleanCart.jsp清除购物车信息:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>清空购物车</title>
</head>
<body>
<%
session.removeAttribute("cart");
session.invalidate();
%>
<a href="/day06/Cart/productsList.jsp">返回购物列表</a>
</body>
</html>
六、浏览器禁用Cookie后session的处理
当客户端禁用了cookie时,造成session无法访问,此时我们可以使用URL重写来解决这个问题
URL重写要求将站点中的所有超链接都进行改造,在超链接后用一个特殊的参数JSESSIONID保存当前浏览器对应session的编号,这样一来,当用户点击超链接访问服务器时,服务器可以从URL后的参数中分析出JSESSIONID,从而找到对应的sesison使用.
URL重写之前,要先创建出session,才能进行重写操作
(1)如果用户禁止cookie,服务器仍会将sessionId以cookie的方式发送给浏览器,但是,浏览器不再保存这个cookie(即sessionId)了。
(2)如果想继续使用session,需要采取其他方式来实现sessionId的跟踪。
可以使用url重写来实现sessionId的跟踪。
(3)url重写
a,什么是URL重写
浏览器在访问服务器上的某个地址时,不能够直接写这个组件的地址,而应该使用服务器生成的这个地址。
比如,
<a href="some">someServlet</a> error
<a href-"<%=response.encodeURL("some")%>"></a>
//encodeURL方法会在"some"后面添加sessionId。
b,如何进行url重写。
//encodeURL方法用在链接地址、表单提交地址。
response.encodeURL(String url);
//encodeRedirectURL方法用于重定向地址。
response.encodeRedirectURL(String url);
购物车案例:
1、创建一个javabean用来保存书籍的信息
2、模拟数据库,用一Map集合将数据信息保存起来
3、在IndexServlet.java 类中用来展示数据的信息并使用response.encodeURL(url)在cookie禁用后对url地址进行重写。
4、BuyServlet.java用来数据的处理,将点击超链接传递过来的数据使用List集合进行保存,并将书籍的信息以重定向的方式传递到ListCartServlet.java类中
5、在ListCartServlet.java获得Session对象和Lsit集合,取出书籍的内容
IndexServlet.java
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>");
System.out.println(request.getContextPath());
// 得到所有的图书集合map
Map<String, Book> map = DB.getAll_DB();
//得到map集合中的key值。map集合中的key值和书籍的id值是保持一致的
Set<String> keys = DB.getAll_DB().keySet();
for (String key : keys) {
Book book = map.get(key);
String url = request.getContextPath() + "/buy?id=" + book.getId();
// response. encodeURL(java.lang.String
// url)用于对表单action和超链接的url地址进行重写
url = response.encodeURL(url);// 将超链接的地址进行重写
out.write(book.getName() + "<a href='" + url + "'>点击购买</a><br>");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
BuyServlet.java
public class BuyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 根据超链接传递过来的id值获得
String id = request.getParameter("id");
// 根据id得到要购买的书籍
Book book = DB.getAll_DB().get(id);
HttpSession session = request.getSession();
List<Book> list = (List<Book>) session.getAttribute("list");
if (list == null) {
list = new ArrayList<Book>();
session.setAttribute("list", list);
}
list.add(book);
// response. encodeRedirectURL(java.lang.String
// url);用于对sendRedirect方法后的url地址进行重写
String url = request.getContextPath() + "/cart1";
System.out.println(url);
response.sendRedirect(url);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ListCartServlet.java
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();
List<Book> list = (List<Book>) session.getAttribute("list");
if (list == null && list.size() > 0) {
out.write("您还没有购买任何商品");
out.write("<a href='" + request.getContextPath() + "/index'>点击返回购买商品列表</a>");
}
out.write("您购买的商品如下:<br>");
for (Book book : list) {
out.write(book.getName() + "<br>");
}
out.write("<a href='" + request.getContextPath() + "/index'>点击返回购买商品列表</a>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
七、session对象的销毁和过期
1、在Tomcat9.0\apache-tomcat-9.0.13\conf下面的 web.xml中配置session的过期时间。
2、在程序中setMaxInactiveInterval(int interval)方法过期时间。
session.setMaxInactiveInterval(30*60);
3、服务器端调用了HttpSession的invalidate()方法销毁session。
1 HttpSession session = request.getSession();
2 //手工调用session.invalidate方法,摧毁session
3 session.invalidate();
参考链接:
HTTP无状态协议和cookie、session原理
用户禁止cookie后,如何继续使用session
JavaWeb学习总结(十二)——Session