一、会话
1. 基本介绍
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个Web资源,然后关闭浏览器,整个过程称之为一个会话
2. 会话的过程出现的问题
- 每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据
- 例如:多个用户点击超链接通过一个Servlet各自购买了一个商品,服务器应该想办法把每一个用户购买的商品保存在各自的地方,以便于这些用户点结帐Servlet时,结帐Servlet可以得到用户各自购买的商品为用户结帐
3. 会话的两种技术
- Cookie
- Session
二、Cookie
1. Cookie有什么用
大家在访问某个网站的时候,是否能看到提示你上次登录网站的时间,而且要注意的是不同,用户上次登录的时间肯定是不一样的,这是怎么实现的,使用Cookie
大家在访问某个购物网站的时候,是否能看到提示你曾经浏览过的商品,不同用户浏览过的商品肯定不一样,这是怎么实现的,使用Cookie
2. Cookie技术
Cookie(小甜饼)是客户端技术,服务器把每个用户的数据以Cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的Web资源时,就会带着各自的数据去。这样Web资源处理的就是用户各自的数据了
访问百度,F12选择应用程序就能看到
Cookie是服务器在客户端保存用户的信息,比如登录名,浏览历史等, 就可以以Cookie方式保存
Cookie信息就像是小甜饼(cookie 中文)一样,数据量并不大,服务器端在需要的时候可以从客户端/浏览器读取(http 协议),可以通过图来理解
3. Cookie可以用来做啥
- 保存上次登录时间等信息
- 保存用户名,密码, 在一定时间不用重新登录
- 网站的个性化,比如定制网站的服务,内容
4. Cookie基本使用
可以参考这个文档:
Cookie有点象一张表(K-V),分两列,一个是名字,一个是值,数据类型都是 String , 如图
1.如何创建一个Cookie(在服务端创建的)
Cookie cookie=new Cookie(String name,String val);
//保存时间
cookie.setMaxAge();
2.如何将一个Cookie添加到客户端
response.addCookie(c);
3.如何读取Cookie(在服务器端读取到 cookie 信息)
request.getCookies();
5. Cookie的案例
演示Cookie底层实现机制,创建和读取Cookie
public class SetCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建一个Cookie对象
//username是cookie的名字,名字要唯一,可以理解为是一个key
//可以创建多个cookie对象
Cookie username = new Cookie("username", "olivier");
Cookie password = new Cookie("password", "pwd");
//2.设置response类型和编码
response.setContentType("text/html; charset=utf-8");
//3.将Cookie发送给浏览器,让浏览器将该Cookie保存起来
response.addCookie(username);
response.addCookie(password);
//4.writer写入返回
PrintWriter writer = response.getWriter();
writer.write("<h1>创建cookie成功</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
这样就包含了这些Cookie
看一下后台
和上面说的一致
读取Cookie
public class ReadCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.通过request读取cookie
Cookie[] cookies = request.getCookies();
//2.遍历cookies
String msg = "";
if (cookies != null && cookies.length != 0) {
for (Cookie cookie : cookies) {
msg += cookie.getName() + "-->" + cookie.getValue() + "\r\n";
System.out.println(cookie.getName() + "-->" + cookie.getValue());
}
}
//3.给浏览器返回信息
response.setContentType("text/html; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("<h1>" + msg + "</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
需求2:在服务器端读取指定的Cookie,给定Cookie的name,返回Cookie的值,如果不存在该Cookie,返回null
写一个Util
public class CookieUtils {
//返回指定的cookie值
public static Cookie readCookieByName(String name, Cookie[] cookies){
//遍历
if (name == null || "".equals(name) || cookies == null || cookies.length == 0) {
return null;
}
for (Cookie cookie : cookies){
if (name.equals(cookie.getName())){
return cookie;
}
}
return null;
}
}
获取对应的Cookie
public class ServletGetOne extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
Cookie cookie = CookieUtils.readCookieByName("username", cookies);
if (cookie != null) {
System.out.println(cookie.getName() + " -- " + cookie.getValue());
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
需求:修改cookie,给定一个cookie的name,如果没找到,提示没有这个cookie,如果有,就修改它的值
public class UpdateCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
Cookie cookie = CookieUtils.readCookieByName("username", cookies);
response.setContentType("text/html; charset=utf-8");
PrintWriter writer = response.getWriter();
if (cookie != null){
cookie.setValue("newOlivier");
response.addCookie(cookie);
writer.write("<h1>修改成功!</h1>");
}else {
writer.write("<h1>没有这个cookie</h1>");
}
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
注意需要修改Cookie的话,需要set后再add回去,这样浏览器才能保存信息
另外如果创建一个同名的Cookie,就相当于覆盖
6. JSESSIONID
JSessionID是用来确定是哪个用户来访问服务器的,不同的用户JSessionID不同,而且不同的会话,JSessionID也不同,要验证新的会话,JSessionID不同,需要关闭浏览器重新打开才行
7. Cookie的生命周期
Cookie的生命周期指的是如何管理Cookie什么时候被销毁
setMaxAge()
- 正数,表示在指定的秒数后过期
- 负数,表示浏览器关闭,Cookie就会被删除(默认值是-1)
- 0,表示马上删除Cookie
如果不设置Cookie的声明周期的话,默认setMaxAge(-1) ,也就是关闭
需求:演示Cookie的生命周期
public class LifeCycleServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie = new Cookie("lifeCycle", "testLifeCycle");
//设置为30s,则30s之后就失效了,可能不会立刻从浏览器删除,但是关闭浏览器会消失
//cookie.setMaxAge(30);
//创建完立刻被删除
//cookie.setMaxAge(0);
//创建完除非关闭浏览器才会删除
cookie.setMaxAge(-3);
response.addCookie(cookie);
System.out.println("add结束");
}
}
同理,要想删除Cookie,需要设置cookie.setMaxAge(0)
,这样
8. Cookie的有效路径
有效路径规则:Cookie的path属性可以有效的过滤哪些Cookie可以发送给服务器,哪些不发。path属性是通过请求的地址来进行有效的过滤
规则如下:
如果设置了如下规则
- cookie1.setPath = /工程路径
- cookie2.setPath = /工程路径/aaa
- cookie1会发给服务器
- cookie2不会发给服务器
- cookie1 会发给服务器
- cookie2 会发给服务器
需求:演示上面这个例子
public class CookiePathServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie1 = new Cookie("cookie1","cookie1Instance");
Cookie cookie2 = new Cookie("cookie2","cookie2Instance");
cookie1.setPath(request.getContextPath());
System.out.println("contextpath = " + request.getContextPath());
cookie2.setPath(request.getContextPath() + "/aaa");
response.addCookie(cookie1);
response.addCookie(cookie2);
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("设置路径成功");
writer.flush();
writer.close();
}
}
当在读取的时候,规则是这样的,请求路径如果比设置的路径更具体,则Cookie会被请求到,如果更宽泛或者根本不匹配则Cookie请求不到
9. Cookie的注意事项和细节
-
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
-
一个WEB站点可以给一个浏览器发送多个Cookie,一个浏览器也可以存储多个WEB站点提供的Cookie。
-
Cookie的总数量没有限制,但是每个域名的COOKIE数量和每个COOKIE的大小是有限制(不同的浏览器限制不同, 知道即可),Cookie不适合存放数据量大的信息
-
注意,删除Cookie时,path必须一致,否则不会删除
-
Java servlet中Cookie中文乱码解决办法:如果存放中文的Cookie,默认报错,可以通过URL编码和解码来解决,不建议存放中文的Cookie信息
public class SetChineseCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String lisi = URLEncoder.encode("李四", "UTF-8");
String lisiName = URLEncoder.encode("李四的名字", "UTF-8");
Cookie cookie = new Cookie(lisi,lisiName);
response.addCookie(cookie);
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("中文Cookie设置成功");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
中文的Cookie会报错
如果通过URLEncoder编码后再传入即可:
解码使用URLDecoder:
public class GetChineseCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
String cookieName = URLEncoder.encode("李四", "UTF-8");
Cookie cookie = CookieUtils.readCookieByName(cookieName, cookies);
if (cookie != null){
String value = cookie.getValue();
String decodeValue = URLDecoder.decode(value,"utf-8");
System.out.println("value = " + decodeValue);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
三、Session
1. Session有什么用
不同的用户登录网站后,不管该用户浏览该网站的哪个页面,都可显示登录人的名字,还可以随时去查看自己的购物车中的商品, 是如何实现的?
也就是说,一个用户在浏览网站不同页面时,服务器是如何知道是张三在浏览这个页面,还是李四在浏览这个页面?
Cookie不太可行,虽然可以实现功能,但是不能存储过大的信息,也不应存放敏感信息,而且它是和浏览器走的,在公共场合可能存在泄露隐私的危险
2. Session技术
Session是服务器端技术,服务器在运行时为每一个用户的浏览器创建一个其独享的Session对象/集合
由于Session为各个用户浏览器独享,所以用户在访问服务器的不同页面时,可以从各自的Session中读取/添加数据, 从而完成相应任务
3. Session基本原理
当用户打开浏览器,访问某个网站,操作Session时,服务器就会在内存(在服务端)为该浏览器分配一个Session对象,该Session对象被这个浏览器独占, 如上图
这个Session对象也可看做是一个容器/集合,Session对象默认存在时间为30min(这是在tomcat/conf/web.xml),也可修改
服务器是怎么区分服务器中的Session是属于哪个用户的呢?通过JSESSIONID来确定
3. Session可以用来做啥
- 网上商城中的购物车
- 保存登录用户的信息
- 将数据放入到Session中,供用户在访问不同页面时,实现跨页面访问数据
- 防止用户非法登录到某个页面
- .....
4. 如何理解Session
Session存储结构示意图
你可以把Session看作是一容器类似HashMap,有两列(K-V),每一行就是Session的一个属性
每个属性包含有两个部分,一个是该属性的名字(String),另外一个是它的值(Object)
5. Session的常用方法
- 创建和获取Session
HttpSession hs=request.getSession();
第1次调用是创建Session会话,之后调用是获取创建好的Session对象
- 向Session添加属性
hs.setAttribute(String name,Object val);
- 从Session得到某个属性
Object obj=hs.getAttribute(String name);
- 从Session删除调某个属性
hs.removeAttribute(String name);
- 判断是不是刚创建出来的Session
isNew();
每个Session都有1个唯一标识Id值。通过getId()得到Session的会话id值,也就是JSESSIONID,通过它区分每个用户的Session
6. Session底层实现机制
更细节的图解
7. Session案例
public class CreateSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=utf-8");
//1.获取Session
HttpSession session = request.getSession();
//2.获取SessionID
System.out.println("sessionID=" + session.getId());
//3.存数据
session.setAttribute("email","zhangsan@qq.com");
//4.回复
PrintWriter writer = response.getWriter();
writer.write("<h1>创建session成功</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
会发现浏览器的JSESSIONID和后台的一致
第一次请求会出现set-Cookie:JSESSIONID,第二次请求就只在请求里携带了,响应就不带了,因为第一次没有创建需要创建并返回给浏览器,第二次已经有JSESSIONID,所以请求的时候就携带了,响应的时候就没有了
获取Session
public class ReadSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 获取session
HttpSession session = request.getSession();
//2. 读取属性
Object email = session.getAttribute("email");
if (email != null){
System.out.println("session属性email = " + (String) email);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
8. Session生命周期
public void setMaxInactiveInterval(int interval)
设置Session的超时时间(以秒为单位),超过指定的时长,Session就会被销毁- 值为正数的时候,也就是设定Session的超时时长
- 负数表示永不超时
public int getMaxInactiveInterval()
获取Session的超时时间public void invalidate()
让当前Session会话立即无效- 如果没有调用
setMaxInactiveInterval()
来指定Session的生命时长,Tomcat会以Session默认时长为准,Session默认的超时为30分钟, 可以在Tomcat的web.xml设置 - Session的生命周期指的是:客户端/浏览器两次请求最大间隔时长,而不是累积时长。即当客户端访问了自己的 session,session 的生命周期将从 0 开始重新计算(指的是同一个会话两次请求之间的间隔时间)
- 声明周期怎么维护的,底层:Tomcat用一个线程来轮询会话状态,如果某个会话的空闲时间超过设定的最大值,则将该会话销毁
案例:
创建Session
public class CreateSession2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
System.out.println("sessionId = " + session.getId());
session.setMaxInactiveInterval(60);
session.setAttribute("username","zhangsan");
response.setContentType("text/html; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("<h1>创建session2成功,生命周期是60s</h1>");
writer.flush();
writer.close();
}
}
读取Session
public class ReadSession2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
Object username = session.getAttribute("username");
if (username != null){
System.out.println("username = " + (String) username);
}
response.setContentType("text/html; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("<h1>username = " + (String) username + "</h1>");
writer.flush();
writer.close();
}
}
读取完等60s,发现属性已经没有了
测试删除
public class ServletDelete extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.invalidate();
response.setContentType("text/html; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("delete success");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
session.invalidate();
相当于直接删除session的所有属性,如果只想删除某一个Session属性,就使用session.removeAttribute("xxx");