1、HTTP协议的特点
⑴ 纯文本
⑵ 无状态
无状态是指服务器不能区分多个请求是否来自同一个用户,但是有时我们需要让服务器知道多个请求是来自同一个用户。例如:网上购物
这时就需要用到Cookie和Session
2、Cookie
概念
Cookie是服务器发送给浏览器的,用于区分不同用户的一段信息
运行机制
⑴ 浏览器第一次发送请求给服务器时,服务器会创建Cookie对象
⑵ 服务器将Cookie对象发送给浏览器
⑶ 以后浏览器再次发送请求给服务器时,浏览器就会携带该Cookie对象
⑷ 服务器就会根据不同的Cookie对象,来区分不同的用户
相关方法
创建Cookie对象
public Cookie(String name, String value) {}
第一个参数:Cookie对象的名字;第二个参数:Cookie对象的值
将Cookie对象发送给浏览器
public void addCookie(Cookie cookie);
用HttpServletResponse来调用,需要传递一个Cookie对象
注意事项:
⑴ Cookie对象的名字不能使用中文
⑵ Cookie对象的值可以使用中文,但是需要使用字符集进行编码,所以不建议使用中文
⑶ 创建Cookie对象后,一定要用HttpServletResponse来调用addCookie方法,否则浏览器是无法保存该Cookie对象的,因为没有给浏览器,就等同于白创建Cookie对象
示例:
Cookie cookie = new Cookie("username", "admin");
response.addCookie(cookie);
创建一个Cookie对象,并发送给浏览器
获取Cookie对象
public Cookie[] getCookies();
用HttpServletResponse来调用,获取保存所有Cookie对象的数组
获取Cookie对象的值
⑴ 获取Cookie对象的名字
public String getName() {
return name;
}
通过Cookie对象来调用,获取其名字
⑵ 获取Cookie对象的值
public String getValue() {
return value;
}
通过Cookie对象来调用,获取其值
示例:
Cookie[] cookies = request.getCookies();
// 判读是否有Cookie
if (null != cookies) {
int len = cookies.length;
for (int i = 0; i < len; i++) {
Cookie cookie = cookies[i];
System.out.println("名字:" + cookie.getName() + ",值:" + cookie.getValue());
}
}
获取所有的Cookie对象的名字和值
修改Cookie对象
⑴ 方式一:
创建一个同名的Cookie对象,这样会将之前的Cookie的值覆盖掉
⑵ 方式二:
调用getCookies 方法,遍历所有的Cookie对象,找出指定名字的Cookie对象,并调用setValue方法
public void setValue(String newValue) {
value = newValue;
}
注意:调用setValue 方法后,一定要调用HttpServletResponse的addCookie 方法,将修改后的Cookie发送给浏览器,否则等同于没有修改
示例:
【修改名叫”username”的Cookie对象的值】
方式一:
Cookie cookie = new Cookie("username", "newAdmin");
response.addCookie(cookie);
方式二:
Cookie[] cookies = request.getCookies();
// 判读是否有Cookie
if (null != cookies) {
int len = cookies.length;
for (int i = 0; i < len; i++) {
Cookie cookie = cookies[i];
// 查找是否有指定名字的Cookie对象
if ("username".equals(cookie.getName())) {
cookie.setValue("newAdmin");
// 发送修改后的Cookie对象给浏览器
response.addCookie(cookie);
}
}
}
持久化Cookie对象
Cookie对象默认的是会话级别的
会话级别即浏览器一旦关闭,Cookie对象就会失效
通过调用setMaxAge 方法来设置Cookie对象的有效时间
public void setMaxAge(int expiry) {
maxAge = expiry;
}
需要传入有效时间,单位是秒
maxAge > 0 时:Cookie对象在maxAge秒后失效
maxAge = 0时:Cookie对象立即失效
maxAge < 0时,默认【int maxAge = -1;】会话级别的Cookie
注意:该方法需要在调用addCookie 方法之前,被调用,否则无效
示例:
Cookie cookie = new Cookie("timeCookie1", "60sCookie");
cookie.setMaxAge(60);
response.addCookie(cookie);
创建一个有效时间为60秒的Cookie
Cookie cookie = new Cookie("timeCookie2", "defaultCookie");
cookie.setMaxAge(0);
response.addCookie(cookie);
创建一个立即失效的Cookie
Cookie cookie = new Cookie("timeCookie3", "defaultCookie");
// cookie.setMaxAge(-1);
response.addCookie(cookie);
创建一个默认【会话级别】的Cookie
设置Cookie的有效路径
Cookie对象的有效路径,即访问哪些地址时,浏览器会携带对应的Cookie对象
默认的有效路径是当前WEB应用的根路径,可以通过setPath 方法来指定Cookie对象的有效路径
public void setPath(String uri) {
path = uri;
}
需要传入一个地址。注意该地址由浏览器解析
示例:
Cookie cookie = new Cookie("pathCookie1", "defaultPathCookie");
response.addCookie(cookie);
创建一个有效路径为当前WEB应用的根目录的Cookie
Cookie cookie = new Cookie("pathCookie2", "helloPathCookie");
// 该路径由浏览器解析
cookie.setPath(request.getContextPath() + "/hello");
response.addCookie(cookie);
创建一个有效路径为 当前WEB应用根目录/hello 的Cookie
Cookie的用途
⑴ 广告推荐
⑵ 免登陆【设置Cookie的有效时间】
Cookie的不足
⑴ Cookie的键和值都是明文的,不安全
⑵ 不同的浏览器对Cookie的大小和数量有限制
⑶ 携带过多的Cookie对象会费流量
⑷ Cookie对象中的值只能保存字符串(String)
3、Session
概念
Session即HttpSession,其用来保存用户的信息
运行机制
⑴ 服务器创建Session对象,该对象有一个全球唯一的ID
⑵ 在创建Session对象的同时,会创建一个特殊的Cookie,该Cookie对象的名字是一个固定值:JSESSIONID。这个Cookie对象的value值,就是Session对象所拥有的全球唯一的ID。服务器会将这个Cookie发送给浏览器
⑶ 以后浏览器再次请求服务器时,就会携带这个特殊的Cookie对象
⑷ 服务器获取这个特殊的Cookie对象的value值后,就会在服务器中寻找与之对应的Session对象,并以此来区分不同的用户
相关方法
创建Session对象
⑴ 如果访问的是JSP页面
一旦访问JSP页面,就会创建Session对象。这个Session对象是在JSP文件对应的 .java 文件中的 _jspService 方法中创建的,通过调用 pageContext.getSession();
⑵ 如果访问的是HTML页面
这时只有访问Servlet时,通过调用HttpServletRequest的getSession方法时,才会创建Session对象
public HttpSession getSession();
Tips:可以通过调用HttpSession的getId 方法,来获取保存在JSESSIONID这个特殊的Cookie对象的value的值
public String getId();
注意:在一次会话中,只能使用一个Session对象。Session对象一旦被创建,则在整个会话中,无论在哪获取到的,都是同一个Session对象
通过持久化特殊的Cookie对象JSESSIONID,来达到找到Session中保存的用户的目的
由于Cookie对象默认是会话级别的,所以一旦浏览器被关闭,该Cookie对象就会失效
所以,一旦浏览器被关闭,特殊的Cookie对象:JSESSIONID,就会失效,同时就无法找到与之对应的Session对象,也就找不到Session中保存的用户
对此我们可以通过持久化JSESSIONID,这个特殊的Cookie对象,来找到之前保存到Session中的用户的目的
示例:
// 获取所有的Cookie对象
Cookie[] cookies = request.getCookies();
if (null != cookies) {
int len = cookies.length;
// 遍历
for (int i = 0; i < len; i++) {
Cookie cookie = cookies[i];
String cookieName = cookie.getName();
// 匹配名字
if ("JSESSIONID".equals(cookieName)) {
// 设置有效时间
cookie.setMaxAge(60);
// 发送给浏览器
response.addCookie(cookie);
}
}
}
设置Session对象的最大空闲时间
Session对象的最大空闲时间默认是30分钟,在tomcat服务器的总的配置文件web.xml中,有设置
我们也可以手动地设置Session对象的最大空闲时间。需要调动setMaxInactiveInterval 方法
public void setMaxInactiveInterval(int interval);
需要传入设置Session的最大空闲时间的参数,单位是秒
interval > 0:Session对象在interval秒后失效
interval = 0:Session对象立即失效
interval < 0:Session对象永不失效
注意:传入0时,在tomcat7.0中不好用。推荐使用invalidate方法,该方法是让指定的Session对象立即失效
public void invalidate();
示例:
HttpSession session = request.getSession();
session.setMaxInactiveInterval(3);
设置Session对象的最大空闲时间为3秒
HttpSession session = request.getSession();
/*
* tomcat7.0不好使
*
* session.setMaxInactiveInterval(0);
*/
session.invalidate();
设置Session对象立即失效
HttpSession session = request.getSession();
session.setMaxInactiveInterval(-1);
设置Session对象永不失效
4、钝化和活化
钝化
Session和Session域中保存的对象一起从内存中被序列化到硬盘上的过程
服务器关闭的时候,会发生钝化的现象
会在:
工作空间\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\项目名
下面生成一个:SESSIONS.ser 文件
活化
Session和Session域中保存的对象一起从硬盘中被反序列化到内存中的过程
服务器再次被开启的时候,会发生活化的现象
注意事项
⑴ 要保证正常的钝化和活化,Session域中保存的对象,所对应的类要实现Serializable【序列化】接口。如果其内部有其他类型,则对应的类也需要实现Serializable接口,否则整个对象都不能被反序列化
⑵ 如果服务器不是正常地被关闭。例如点击了Console窗口的Terminate按钮,这时就无法实现活化,因为钝化并没有实现【服务器不是正常地被关闭的】
5、URL重写
为啥要重写URL
当浏览器禁用Cookie时,每次向服务器发起请求的时候,就不会携带Cookie对象,也就无法获取对应的Session对象
解决办法
⑴ 可以调用HttpServletResponse对象的encodeURL方法
public String encodeURL(String url);
将要跳转的url传入,如果浏览器禁用了Cookie,则会在URL地址的后面携带JSESSIONID。如果浏览器没有禁用Cookie,则URL地址后面不会携带JSESSIONID
URL地址;jsessionid=???
⑵ 可以调用HttpServletResponse对象的encodeRedirectURL方法
public String encodeRedirectURL(String url);
该方法和encodeURL类似
⑶ 可以使用JSTL的
其中value属性就是要跳转的页面的URL地址。该标签返回一个URL地址
示例
<a href="<%=response.encodeRedirectURL("pages/show.jsp")%>">方式一</a><br /><br />
<a href="<%=response.encodeRedirectURL("pages/show.jsp")%>">方式二</a><br /><br />
<a href="<c:url value="pages/show.jsp"></c:url>">方式三</a><br /><br />
6、 表单的重复提交
概念
同一个表单、同样的数据,向服务器提交多次
危害:
⑴ 给服务器造成了不必要的压力
⑵ 可能会造成数据库中保存了很多的垃圾数据
重复提交的三种情况
转发的情况下,表单提交成功后,多次刷新成功页面
⑴ 产生的原因
转发的情况,因为浏览器的地址栏中的地址并没有改变,所以多次刷新,都相当于重复地向action所指向的资源文件处提交请求
⑵ 解决方案
改用重定向
使用示例
【登录JSP页面】
<h1>转发,成功后多次刷新成功界面</h1>
<form action="<%=request.getContextPath()%>/loginServlet" method="post">
用户名:<input type="text" name="username" />
<input type="submit" value="登录" />
</form>
【LoginServlet】
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("接收用户请求");
// request.getRequestDispatcher("/success.jsp").forward(request, response);
// 改为重定向
response.sendRedirect(request.getContextPath() + "/success.jsp");
}
在网速很慢的情况下,多次单击表单的提交按钮
⑴ 产生的原因
提交按钮可以被单击多次
⑵ 解决方案
让提交按钮只能单击一次
使用示例
【登录JSP页面】
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
$(function(){
$(":submit").click(function(){
// 将提交按钮禁用
$(this).attr("disabled", true);
// 提交表单
$("form").submit();
});
});
</script>
</head>
<body>
<h1>网速慢,多次单击提交按钮</h1>
<form action="<%=request.getContextPath()%>/loginServlet" method="post">
用户名:<input type="text" name="username" />
<input type="submit" value="登录" />
</form>
</body>
【LoginServlet】
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 模拟网速慢
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接收用户请求");
response.sendRedirect(request.getContextPath() + "/success.jsp");
}
表单提交成功后,单击浏览器的后退按钮,在不刷新页面的情况下,再次单击提交按钮
⑴ 产生的原因
服务器可以处理重复提交的表单
⑵ 解决方案
使用token(记号、令牌)
实现的步骤:
① 在服务器中生成一个全球唯一的字符串作为token,并将其保存到Session域中
全球唯一的字符串,可以使用java.util.UUID类的方法来创建。UUID是一个32位的字母、数字混排的由机器码和时间戳计算得到的一个全球唯一的字符串
public static UUID randomUUID() {}
获取一个UUID。这是一个静态方法,可以使用UUID来调用
Tips:可以调用UUID的toString方法,将其转换为字符串
② 并将token放到表单的隐藏域中,随着表单一起提交到服务器
③ 在服务器获取Session域中的token,并从隐藏域中获取token,两个对比
⒈ 如果相等,就是正常提交
⒉ 如果不等,就是重复提交
④ 正常的提交处理完后,一定要将Session域中的token移除
使用示例
【登录JSP页面】
<%
// 创建一个UUID字符串
String uuid = UUID.randomUUID().toString();
// 放到Session域中
session.setAttribute("sessionUUID", uuid);
%>
<h1>提交成功后,单击浏览器的返回按钮,不刷新页面,再次单击提交按钮</h1>
<form action="<%=request.getContextPath()%>/loginServlet" method="post">
<input type="hidden" name="requestUUID" value="<%=uuid%>" />
用户名:<input type="text" name="username" />
<input type="submit" value="登录" />
</form>
【LoginServlet】
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取隐藏域中的UUID
String requestUUID = request.getParameter("requestUUID");
// 获取Session对象
HttpSession session = request.getSession();
// 获取Session域中的UUID
String sessionUUID = (String) session.getAttribute("sessionUUID");
if (null != sessionUUID && requestUUID.equals(sessionUUID)) {
// 正常提交
// 一定要移除Session中保存的UUID!
session.removeAttribute("sessionUUID");
System.out.println("接收用户请求");
response.sendRedirect(request.getContextPath() + "/success.jsp");
} else {
// 重复提交,则重定向到其他页面
response.sendRedirect(request.getContextPath() + "/failure.jsp");
}
}