会话跟踪技术
-
什么是会话
会话是指一个终端用户(服务器)与交互系统(客户端)进行通讯的过程。 -
什么是会话跟踪
对同一个用户对服务器的连续的请求和接受响应的监视。(将用户与同一用户发出的不同请求之间关联,为了数据共享) -
为什么需要会话跟踪
浏览器与服务器之间的通信是通过HTTP协议进行通信的,而HTTP协议是”无状态”的协议,它不能保存客户的信息,即一次响应完成之后连接就断开了,下一次的请求需要重新连接,这样就需要判断是否是同一个用户,所以才应会话跟踪技术来实现这种要求 -
四种会话跟踪技术
a) URL重写:
URL(统一资源定位符)是Web上特定页面的地址,URL地址重写的原理是将该用户Session的id信息重写 到URL地址中,以便在服务器端进行识别不同的用户。URL重写能够在客户端停用cookies或者不支持cookies的时候仍然能够发挥作用。
DemoBServlet.java
package servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
@WebServlet("/B")
public class DemoBServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");//URL重写
System.out.println(name);
name = URLDecoder.decode(name,"utf-8");
System.out.println(name);
}
}
EncodeUtil.java
package util;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class EncodeUtil {
//转码
public static String encodingString(String str){
try{
str = URLEncoder.encode(str,"utf-8");
}catch(Exception e){
System.out.println(e.getMessage());
}
return str;
}
//解码
public static String decodeString(String str){
try{
str = URLDecoder.decode(str,"utf-8");
}catch(Exception e){
System.out.println(e.getMessage());
}
return str;
}
public static void main(String[] args) {
String str = encodingString("你好");
System.out.println(str);
str = decodeString(str);
System.out.println(str);
}
}
b.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script>
function funA(){
var val = document.getElementById("name").value;
alert(val);
val = encodeURI(val);
alert(val);
val = encodeURI(val);//浏览器解码一次,不再编码一次的话输出到控制台就是两次中文
alert(val);
location.href = "B?name="+val;
}
</script>
</head>
<body>
<input type="text" id="name"/>
<button onclick="funA()">发送</button>
</body>
</html>
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<welcome-file-list>
<welcome-file>b.jsp</welcome-file>
</welcome-file-list>
</web-app>
b) 隐藏表单域:
将会话ID添加到HTML表单元素中提交到服务器,此表单元素并不在客户端显示,浏览时看不到,源代码中有。
DemoAServlet.java
package servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
@WebServlet("/A")
//隐藏域表单
/*
a1提交表单时,a2如果不接收a1的数据并转发出去的话,在a2中想显示a1的东西
,就不会显示,必须将a1发送过来的东西也接收了并发送出去。
*/
public class DemoAServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println(name);
}
}
a1.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="a2.jsp" method="post">
<input type="text" name="name" placeholder="请输入姓名"/>
<button>姓名</button>
</form>
</body>
</html>
a2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>//注意要将siELIgnored属性设置成false 才能使用el表达式
<html>
<head>
<title>Title</title>
</head>
<body>
${param.name}:${param.content}
<hr/>
<form action="a2.jsp" method="post">
<input type="text" name="content" placeholder="请输入内容"/>
<input type="hidden" name="name" value="${param.name}"/><!--不再次转发的话会读不到-->
<button>发送</button>
</form>
</body>
</html>
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<welcome-file-list>
<welcome-file>a1.jsp</welcome-file>
</welcome-file-list>
</web-app>
c) Cookie:
Cookie是Web服务器发送给客户端的一小段信息,客户端请求时可以读取该信息发送到服务器端,进而进行用户的识别。对于客户端的每次请求,服务器都会将Cookie发送到客户端,在客户端可以进行保存,以便下次使用。 服务器创建保存于浏览器端,不可跨域名性,大小及数量有限。客户端可以采用两种方式来保存这个Cookie对象,一种方式是 保存在 客户端内存中,称为临时Cookie,浏览器关闭后 这个Cookie对象将消失。另外一种方式是保存在 客户机的磁盘上,称为永久Cookie。以后客户端只要访问该网站,就会将这个Cookie再次发送到服务器上,前提是 这个Cookie在有效期内。 这样就实现了对客户的跟踪。
Cookie是可以被禁止的。
DemoCServlet.java
package servlet;
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;
import java.io.IOException;
@WebServlet("/C")
public class DemoCServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");//设置请求的编码集
String name = req.getParameter("name");
String pass = req.getParameter("pass");
String is = req.getParameter("is");
if ("admin".equalsIgnoreCase(name)&&"a123".equals(pass)){
if (is!=null){//选中记住密码
Cookie names = new Cookie("USER_NAME",name);
names.setMaxAge(60*60*24);
resp.addCookie(names);
Cookie passes = new Cookie("USER_PASS",pass);
passes.setMaxAge(60*60*24);
resp.addCookie(passes);
}
resp.sendRedirect("index.jsp");
}else{
resp.sendRedirect("c.jsp?error=1");
}
}
}
c.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<c:if test="${param.error==1}">
你的账号或密码错误!
</c:if>
<form method="post" action="C">
<table border="1">
<tr>
<th>账号</th>
<td><input type="text" name="name" value="${cookie.USER_NAME.value}"/></td>
</tr>
<tr>
<th>密码</th>
<td><input type="password" name="pass" value="${cookie.USER_PASS.value}"/></td>
</tr>
<tr>
<th colspan="2"><input type="checkbox" name="is" value="1"/>是否记住密码</th>
</tr>
<tr>
<th colspan="2"><button>登录</button></th>
</tr>
</table>
</form>
</body>
</html>
<!--
顺便复习下value属性的知识:
input 标签的 value 属性的作用是由 input 标签的 type 属性的值决定的
当 type 的取值为 button、reset、submit 中的其中一个时,此时 value 属性的值表示的是按钮上显示的文本
当 type 的取值为 text、password、hidden 中的其中一个时,此时 value 属性的值表示的是输入框中显示的初始值,
此初始值可以更改,并且在提交表单时,value 属性的值会发送给服务器(既是初始值,也是提交给服务器的值)
当 type 的取值为 checkbox、radio 中的其中一个时,此时 value 属性的值表示的是提交给服务器的值
当 type 的取值为 image 时,点击它提交表单后,会将用户的点击位置相对于图像左上角的 x 坐标和 y 坐标提交给服务器
checkbox 型的 input 标签的不足之处在于:提交表单时,只有处于勾选状态的复选框的数据值才会发送给服务器。也就是说,
如果没有任何一个复选框被选中,那么服务器就不会收到与其相关的数据项
当设置 input 标签的 type 属性值为checkbox 或者 radio 时,必须同时设置 input 标签的 value 属性
当 type="file" 时,不能使用 value 属性
-->
d) session:
每一个用户都有一个不同的session,各个用户之间是不能共享的,是每个用户所独享的,在session中可以存放信息。 保存在服务器端。需要解决多台服务器间共享问题。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。
在服务器端会创建一个session对象,产生一个sessionID来标识这个session对象,然后将这个sessionID放入到Cookie中发送到客户端,下一次访问时,sessionID会发送到服务器,在服务器端进行识别不同的用户。
Session是依赖Cookie的,如果Cookie被禁用,那么session也将失效。
GoodsServlet.java
package servlet;
import bean.GoodsInfo;
import biz.GoodsBiz;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
//goods?type=findAll
@WebServlet("/goods")
public class GoodsServlet extends HttpServlet {
private GoodsBiz gb = new GoodsBiz();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//送过来的参数
String types = req.getParameter("type");
if ("findAll".equals(types)){
findAllGoods(req, resp);
}else if("addgwc".equals(types)){
addGwc(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
private void findAllGoods(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
//获取list集合
List<GoodsInfo> list = gb.findAllGoods();
//将集合放入到session容器中
req.getSession().setAttribute("GOODS_LIST",list);
//跳转页面
resp.sendRedirect("index.jsp");
}
protected void addGwc(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String[] ids = req.getParameterValues("id");
HttpSession session = req.getSession();
List<GoodsInfo> gwcList;
if (session.getAttribute("GWC_LIST")==null){
gwcList = new ArrayList<>();
}else{
gwcList = (List<GoodsInfo>)session.getAttribute("GWC_LIST");
}
for (int i = 0; i < ids.length; i++) {
int id = Integer.parseInt(ids[i]);
GoodsInfo gi = gb.findGoodsById(id);
//如果gi在gwcList中存在,则数量加一,否则将gi放入gwcList
int index = gwcList.indexOf(gi);
if (index == -1){
gwcList.add(gi);
}else{
GoodsInfo g = gwcList.get(index);
g.setGoodsNum(g.getGoodsNum()+1);
}
}
session.setAttribute("GWC_LIST",gwcList);
resp.sendRedirect("index.jsp");
}
}
index.jsp
<%@ page language="java" pageEncoding="utf-8" isELIgnored="false" %><!--配置-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><!--c标签-->
<html>
<body>
<c:if test="${empty sessionScope.GOODS_LIST}">
<c:redirect url="goods?type=findAll"/>
</c:if>
<form action="goods?type=addgwc" method="post">
<table border="1" align="center" width="60%">
<tr>
<th>选择</th>
<th>名称</th>
<th>价格</th>
</tr>
<c:forEach var="gi" items="${sessionScope.GOODS_LIST}">
<tr>
<td><input type="checkbox" name="id" value="${gi.goodsId}"/></td>
<td>${gi.goodsName}</td>
<td>${gi.goodsPrice}</td>
</tr>
</c:forEach>
<tr>
<td><button>选择添加到购物车</button></td>
<td><a href="gwc.jsp">查看购物车</a></td>
</tr>
</table>
</form>
</body>
</html>
gwc.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<table border="1" align="center" width="60%">
<tr>
<th>名称</th>
<th>价格</th>
<th>数量</th>
</tr>
<c:set var="sum" value="0"></c:set>
<c:forEach var="gi" items="${sessionScope.GWC_LIST}">
<tr>
<td>${gi.goodsName}</td>
<td>${gi.goodsPrice}</td>
<td>${gi.goodsNum}</td>
</tr>
<c:set value="${sum+gi.goodsPrice*gi.goodsNum}" var="sum"></c:set>
</c:forEach>
<tr>
<td colspan="3">
<a href="index.jsp">返回</a>
总价:¥${sum}元
</td>
</tr>
</table>
</body>
</html>
GoodsInfoDAO.java
package dao;
import bean.GoodsInfo;
import db.DBManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class GoodsInfoDAO {
private Connection conn = null;
private PreparedStatement ps = null;
private ResultSet rs = null;
public List<GoodsInfo> findAllGoods(){
String sql = "select * from goods where goodsState=1";
List<GoodsInfo> list = new ArrayList<>();
conn = DBManager.getConnection();
try{
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()){
GoodsInfo gi = new GoodsInfo();
gi.setGoodsId(rs.getInt(1));
gi.setGoodsName(rs.getString(2));
gi.setGoodsPrice(rs.getDouble(3));
gi.setGoodsState(rs.getInt(4));
list.add(gi);
}
}catch(Exception e){
System.out.println(e.getMessage());
}finally{
DBManager.close(conn,ps,rs);
}
return list;
}
public GoodsInfo findGoodsById(int id){
GoodsInfo gi = new GoodsInfo();
String sql = "select * from goods where goodsId = ?";
conn = DBManager.getConnection();
try{
ps = conn.prepareStatement(sql);
ps.setInt(1,id);
rs = ps.executeQuery();
if (rs.next()){
gi.setGoodsId(rs.getInt(1));
gi.setGoodsName(rs.getString(2));
gi.setGoodsPrice(rs.getDouble(3));
gi.setGoodsState(rs.getInt(4));
}
}catch(Exception e){
System.out.println(e.getMessage());
}finally{
DBManager.close(conn,ps,rs);
}
return gi;
}
}
其余包此处省略
-
Session和Cookie区别:
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。