概念:
客户端(浏览器)与服务器的会话。
使用原因:
使用原HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分中两次请求是否由一个客户端发出。这样的设计严重阻碍的Web程序的设计。
商场购物流程:
小王第一次去商场购物,商场正在搞优惠活动,每次购买200元以上并且积攒满10次就能领取一个小型洗衣机。小王参加了这个优惠活动。商场给小王一个盖了章的卡片,小王每次来商场购物满足条件,就给盖一个章,盖够10个章就可以领取礼品。其中这个盖了章的卡片就相当于cookie,它用于区分不同的用户。
(1)浏览器请求
(2)服务器创建一个Cookie对象,该Cookie对象会携带用户信息。服务器将该Cookie发送给浏览器。
(3)以后浏览器再次请求时,会携带Cookie对象。
(4)服务器会通过该Cookie对象,区分不同用户。
图示:
protected void doGet(HttpServletRequest request, HttpServletResponse response){
Cookie cookie = new Cookie("xxj","23"); //创建cookie对象
response.addCookie(cookie); //服务器将Cookie对象发送给浏览器
}
protected void doGet(HttpServletRequest request, HttpServletResponse response){
//返回的是一个cookie数组
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
String name = cookie.getName(); //得到cookie的名字
String value = cookie.getValue();//得到cookie的值
//用于获取指定的cookie
/*if("xxj".equals(name)) {
System.out.println("name:"+name+"value:"+value);
}*/
}
}
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
//2.遍历查询name为level的Cookie
if("level".equals(cookie.getName())) {
// 3.将该Cookie的value改为sss
cookie.setValue("sss");
response.addCookie(cookie);
}
}
Cookie cookie = new Cookie("level", "sssss");
response.addCookie(cookie);
//Cookie的name不能为中文,value可以为中文,但要指定底层编码字符集,比较麻烦。
所以建议都用英文。
//URLEncoder.encode("钻石会员", "UTF-8");
//创建Cookie对象
Cookie cookie = new Cookie("level", "s");
Cookie cookie2 = new Cookie("level2", URLEncoder.encode("钻石会员", "UTF-8"));
//将cookie响应给浏览器
response.addCookie(cookie);
response.addCookie(cookie2);
a、将cookie的name设置为中文,出现的错误
b、将cookie的value设置为中文不进行指定字符集出现的问题
如果指定字符集,浏览器中也不会进向中文显示
* Cookie默认有效性:当前会话有效(与浏览器有关,浏览器关闭或换一个浏览器,Cookie失效。)
* 持久化Cookie
* cookie.setMaxAge(ss秒)
ss>0:在ss秒后失效
ss=0:立即失效
ss<0:默认情况
* 注意:一旦设置持久化Cookie,Cookie默认(就是自带的cookie)有效性就不起作用了。
* cookie默认有效路径为:当前项目(/项目命)
* 设置cookie的有效路径
* cookie.setPath()
* 注意:一般设置有效路径在当前项目下的某个路径。 (cookie.setPath(request.getContextPath()+"/demopath");)
网上商城的购物车
保持用户登录状态
7天免登陆
如:7天免登陆|记住密码
/**
* 7天记住密码
* 思路:1. 点击登录
* 2. 在Servlet中获取用户名&密码
* 3. 判断复选框是否选中
* 选中:
* * 分别将用户名&密码存放到Cookie中
* * 持久化Cookie7天
* * 将两个Cookie响应到浏览器端
* 没选中
* * 无
* 4. 在浏览器端,获取两个Cookie,并显示用户名&密码到指定位置。
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//取username&pwd值
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
String ck = request.getParameter("ck");
if(ck != null) {
//选中
// 分别将用户名&密码存放到Cookie中
Cookie cookieUserName = new Cookie("username", username);
Cookie cookiePwd = new Cookie("pwd", pwd);
//持久化Cookie7天
cookieUserName.setMaxAge(60*60*24*7);
cookiePwd.setMaxAge(60*60*24*7);
//将两个Cookie响应到浏览器端
response.addCookie(cookieUserName);
response.addCookie(cookiePwd);
}
}
- Cookie的value只能是String类型,不灵活。
- Cookie存放到客户端浏览器中,不安全。因为Cookie是以明文传送。
- Cookie过多,Cookie是为请求或响应报文发送,无形中增加了网络流量。
- 各个浏览器对Cookie有限制,使用上有局限
使用Cookie有一个非常大的局限,就是如果Cookie很多,则无形的增加了客户端与服务端的数据传输量。而且由于浏览器对Cookie数量的限制,注定我们不能再Cookie中保存过多的信息,于是Session出现。
Session的作用就是在服务器端保存一些用户的数据,然后传递给用户一个名字为JSESSIONID的Cookie,这个JESSIONID对应这个服务器中的一个Session对象,通过它就可以获取到保存用户信息的Session。
如:我们常见的办卡。
健身房的销售人员,先询问你是否在他们的健身房办过健身卡。如果办过卡就不在办。如果没有办过,就去他们健身房填写表格创建个人信息(相当于创建session的对象),之后会产生一个卡号,随之把健身卡给你(相当于cookie对象)。以后客户去健身房健需要拿着健身卡进健身房。这个健身卡就是区分不同的客户。
/* 使用Session对象之前,先通过特殊Cookie的value查询Session的id,从而查询Session对象。
* 如果查询到Session对象,就可以使用
* 如果未找到Session对象,则进行以下步骤:
* 创建Session对象,同时创建特殊的Cookie,该Cookie的name为固定值:JSESSIONID,
该Cookie的value为:session的id。
* 服务器会将该Cookie对象,发送到浏览器端。
* 以后浏览器再次请求时,会携带该Cookie对象。
* 服务器通过Cookie的value,找到相应的Session,通过Session区分不同用户。*/
* 类型:HttpSession
* session创建&获取&修改
* session由服务器创建。
* 获取
* html->Servlet:request.getSession() //从服务器获取session
* jsp:直接获取(session是jsp的内置对象)
* _jspxFactory->pageContext->getSession() -> this.session = ((HttpServletRequest) request).getSession();//直接获取的流程
session获取:
HttpSession session = request.getSession();
//1. 持久化Cookie(name=JSESSIONID)
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
//查询特殊的Cookie对象
//遍历Cookie,通过JSESSIONID找到特殊的Cookie
if("JSESSIONID".equals(cookie.getName())) {
//持久化
cookie.setMaxAge(30);
// cookie.setMaxAge(60*30+30);
response.addCookie(cookie);
break;
}
}
_jspxFactory->pageContext->getSession() -> this.session = ((HttpServletRequest) request).getSession();
_jspxFactory:
类型:
javax.servlet.jsp.JspFactory
public abstract class JspFactory {
private static volatile JspFactory deflt = null;
。。。。。。
public abstract PageContext getPageContext(Servlet servlet,
ServletRequest request, ServletResponse response,
String errorPageURL, boolean needsSession, int buffer,
boolean autoflush);
。。。。。。
}
pageContext
public abstract class PageContext
extends JspContext
{
。。。。。。
public abstract HttpSession getSession();
。。。。。。
}
getSession()
public class PageContextImpl extends PageContext {
。。。。。。
private transient HttpSession session;
。。。。。。
@Override
public HttpSession getSession() {
return session;
}
}
this.session = ((HttpServletRequest) request).getSession()
private void _initialize(Servlet servlet, ServletRequest request,
ServletResponse response, String errorPageURL,
boolean needsSession, int bufferSize, boolean autoFlush) {
this.servlet = servlet;
this.config = servlet.getServletConfig();
this.context = config.getServletContext();
this.errorPageURL = errorPageURL;
this.request = request;
this.response = response;
this.applicationContext = JspApplicationContextImpl.getInstance(context);
if (request instanceof HttpServletRequest && needsSession)
/*
这是我们拿到的session,最终的地方
*/
this.session = ((HttpServletRequest) request).getSession();
if (needsSession && session == null)
throw new IllegalStateException(
"Page needs a session and none is available");
depth = -1;
if (bufferSize == JspWriter.DEFAULT_BUFFER) {
bufferSize = Constants.DEFAULT_BUFFER_SIZE;
}
if (this.baseOut == null) {
this.baseOut = new JspWriterImpl(response, bufferSize, autoFlush);
} else {
this.baseOut.init(response, bufferSize, autoFlush);
}
this.out = baseOut;
setAttribute(OUT, this.out);
setAttribute(REQUEST, request);
setAttribute(RESPONSE, response);
if (session != null)
setAttribute(SESSION, session);
setAttribute(PAGE, servlet);
setAttribute(CONFIG, config);
setAttribute(PAGECONTEXT, this);
setAttribute(APPLICATION, context);
isIncluded = request.getAttribute(
RequestDispatcher.INCLUDE_SERVLET_PATH) != null;
}
* Session默认有效性为:当前会话有效(与浏览器有关,浏览器关闭或换一个浏览器,Cookie失效。)
* Session是会话级别,本质原因:因为Cookie是会话级别。
* 持久化Session
1. 持久化特殊的Cookie(name:JSESSIONID)
2. 持久化Session
* Session默认存活30分钟。
* 设置session的timeout
* <session-config>
<session-timeout>5分钟</session-timeout>
</session-config>
* 设置session的非活动时间
* session.setMaxInactiveInterval(ss秒);
* tomcat7.0
* ss>0:设置session的非活动时间为ss。
* ss<=:设置session永不失效
* tomcat6.0
* ss>0:设置session的非活动时间为ss。
* ss=0:设置session立即失效
* ss<0:设置session永不失效
3. 设置session失效
* session.invalidate(); //session域中的所有内容都失效。如果只是想让一个实效,必须进行单独 移除(session.removeAttribute(String str))
持久化session的两种方法:
web.xml设置
单位:分钟----->局限性高(如:不能表示30.5分钟)
session.setInactiveInterval()
单位:秒
示例:
/**
* 持久化Session
* 1. 持久化Cookie(name=JSESSIONID)
* 2. 判断持久化时间
* 时间<=30分钟:无需设置session的非活动时间(因为session默认存活30分钟)
* 时间>30分钟
* 1. web.xml设置
* 2. session.setInactiveInterval()
* 30.5分钟
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
//1. 持久化Cookie(name=JSESSIONID)
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
//查询特殊的Cookie对象
if("JSESSIONID".equals(cookie.getName())) {
//持久化
cookie.setMaxAge(30);
// cookie.setMaxAge(60*30+30);
response.addCookie(cookie);
break;
}
}
//2. 持久化Session
session.setMaxInactiveInterval(30);
// session.setMaxInactiveInterval(60*30+30);
}
在整个会话控制技术体系中,保持JSESSIONID的值主要通过Cookie实现。但Cookie在浏览器端可能会被禁用,所以我们还需要一些备用的技术手段,例如:URL重写。
URL重写其实就是将JSESSIONID的值以固定格式附着在URL地址后面,以实现保持JSESSIONID,进而保持会话状态。这个固定格式是:URL;jsessionid=xxxxxxxxx
例如:
targetServlet;jsessionid=F9C893D3E77E3E8329FF6BD9B7A09957
实现方式:
示例:
//1.获取Session对象
HttpSession session = request.getSession();
//2.创建目标URL地址字符串
String url = "targetServlet";
//3.在目标URL地址字符串后面附加JSESSIONID的值
url = response.encodeURL(url);
//4.重定向到目标资源
response.sendRedirect(url);
* 钝化:session对象与session内的数据一同从内存序列化到硬盘的过程,称之为钝化。
* 触发时机:关闭服务器时,触发钝化。
* 活化:session对象与session内的数据一同从硬盘反序列化到内存的过程,称之为活化。
* 触发时机:重启服务器时,触发活化。
钝化和活化的数据存放的位置:
注意:
如果session域中的 “对象” 没有进行序列化,在服务器重启之后是读不到数据的。就是不能够反序列化。
示例:
package com.atguigu.bean;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(String name) {
super();
this.name = name;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
//设置session的非活动时间
// session.setMaxInactiveInterval(interval);
//设置session立即失效
// session.invalidate();
//设置session中的studetn对的
session.setAttribute("stu", new Student("zs")); //就是student类需要进行序列化
System.out.println("sessionId:"+session.getId());
}
<body>
<h2>session</h2>
sessionId:<%=session.getId() %><br>
<a href="GetSessionServlet">获取Session</a><br>
<a href="ChiSessionServlet">持久化Session</a><br>
student:${sessionScope.stu }
</body>
表单的重复提交
购物车的实现
登录与登出
* 表单重复提交的条件
1. 转发跳转页面,f5
2. 网速慢,提交表单后,f5
3. 提交表单后,单击回退按钮,再提交。
* 解决表单重复提交问题(让表单只提交一次)
* 使用UUID作为token,解决表单重复提交问题。
* UUID:是一个十六进制的32位的随机数,全球唯一。
* String uuid = UUID.randomUUID().toString().replace("-", "");
* 步骤
1. 生成一个UUID
2. 将UUID分别存放到session域和隐藏域中。
3. 提交表单,分别从两个域中获取UUID
* 判断是否相等
* 相等:提交表单,将Session域中的UUID移除。
* 不等:不提交表单
示例:
<%
//1. 生成一个UUID
String uuid = UUID.randomUUID().toString().replace("-", "");
//将UUID分别存放到session域和隐藏域中。
session.setAttribute("uuid", uuid);
%>
解决表单重复提交
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
3. 提交表单,分别从两个域中获取UUID
* 判断是否相等
* 相等:提交表单,将Session域中的UUID移除。
* 不等:不提交表单
*/
HttpSession session = request.getSession();
//获取session域中的uuid
Object uuid = session.getAttribute("uuid");
//获取隐藏域中uuid
String uuid2 = request.getParameter("uuid2");
//* 判断是否相等
//如果session域中的UUID被移除,则表单已经被提交。就不可能再满足条件。
if(uuid != null && uuid.toString().equals(uuid2)) {
//相等:提交表单,将Session域中的UUID移除。
System.out.println("提交表单");
session.removeAttribute("uuid");
}
System.out.println("end");
}