客户端和服务器通信的过程中,自然而然的会产生一些数据交互。比如,A用户登录了邮箱,那么web服务器该怎么知道C一段时间后的登录状态呢?虽然HttpServletRequest对象和ServletContext对象都可以保存数据,但是不适用于这种情况。
如上图:当浏览器向ServletB发出请求时,它的登陆操作已经完成了,但是却没有留下任何依据能够证明它已经成功登陆。以至于ServletB对他的登陆状态无法判别,这种情况叫用户状态的丢失。造成这种结果的原因是http协议的无状态。
为了解决这个问题,Servlet提供了会话跟踪技术来追踪用户状态,简单的说就是指将用户操作过的重要业务步骤记录下来,以便在后续的处理中使用。
会话跟踪技术有2种:
1、Cookie
2、Session
Cookie是一种会话技术,它用于将会话过程中的数据保存在用户的浏览器中,从而使得浏览器和服务器更好的交互。当服务器向客户端发送Cookie时,会在HTTP响应头字段增加Set-Cookie字段,该字段设置的Cookie遵循一定的规则,以键值对的形式保存,Cookie属性值可以有多个,但是这些属性之间必须以分号和空格分隔。比如:
Set-Cookie:"status='1'; time='2019-04-03 19:05:49'; version=2.5"
那么Cookie是怎么解决我们上面提到的问题的呢?
比如Bob现在登录邮箱,浏览器就会向服务器发送登录的请求,AServlet来处理这个登录请求,验证用户名和密码无误后,就会把Cookie代表登录的键值对发送给浏览器并保存起来。这样,在Cookie的有效期内(默认是会话结束时,即浏览器关闭),如果服务器想要判断Bob是否还在登录状态,只需要从浏览器这里拿到相应的Cookie就可以了。
现在我们来演示一下Cookie的基本操作:
package com.xx.cookie;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class CookieTestServlet
*/
public class CookieTestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public CookieTestServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1.创建Cookie,默认有效期(存活时间)是浏览器会话结束时失效
Cookie cookie = new Cookie("status", "online");
// 2.我们也可以手动设置有效期,以秒为单位
cookie.setMaxAge(100);
// setMaxAge的特殊值:
// -1 :会话Cookie,浏览器关闭时自动删除
// 0 : 表示要删除一个cookie
// 3.将cookie对象添加到response
response.addCookie(cookie);
// 给个cookie创建成功的提示
response.getWriter().println("cookie create success");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
在浏览器中访问这个Servlet:
Cookie设置成功后,我们就可以在浏览器中找到这个Cookie:
现在我们来演示怎么得到Cookie:
package com.xx.cookie;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class GetCookieServlet
*/
public class GetCookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public GetCookieServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取cookie
Cookie[] cookies = request.getCookies();
// 遍历cookie
for (Cookie cookie : cookies) {
response.getWriter().println(
cookie.getName() + "=" + cookie.getValue());
}
//说明:每个cookie对象都有很多属性,我们都可以通过get方法拿到:
//比如说getMaxAge(存活时间),getVersion(版本),getPath(路径)等
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
有时候开发需要,我们需要设置name为中文或者value为中文的Cookie,怎么实现?
这里我们需要用到java.net.URLEncoder.encoder()
和java.net.URLDecoder.decode()
方法:
// 设置中文Cookie,指定用utf-8编码
String cookieName = URLEncoder.encode("姓名", "utf-8");
String cookieValue = URLEncoder.encode("张三", "utf-8");
Cookie chineseCookie = new Cookie(cookieName, cookieValue);
response.addCookie(chineseCookie);
// 处理响应乱码
response.setContentType("text/html;charset=utf-8");
// 获取cookie
Cookie[] cookies = request.getCookies();
// 遍历cookie
for (Cookie cookie : cookies) {
String name = URLDecoder.decode(cookie.getName());
String value = URLDecoder.decode(cookie.getValue());
response.getWriter().println(name + "=" + value);
}
HttpSession是tomcat在服务器端为每个浏览器准备的私人储物箱,每个浏览器在tomcat里都有一个属于自己的HttpSession对象,用于存储私人数据。
在Servlet中可以通过request.getSession(true)方法来获得session对象,这个方法总是返回与发送请求的浏览器对应的session对象。
我们来测试一下:
package com.xx.session;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Servlet implementation class TestSessionServlet
*/
public class TestSessionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public TestSessionServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取session对象,如果没有,就创建一个
HttpSession session = request.getSession(true);
// 获取session的id
response.getWriter().println(session.getId());
// 判断session是新的还是旧的
response.getWriter().println(session.isNew());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
通过上面的例子我们可以发现,尽管所有的请求都会调用getSession方法,但是每个浏览器都有一个属于自己的session对象,并且只在第1次请求时,创建新对象。
总结Session的特点:
1、session 和 client浏览器一一对应(IE/Chrome/FF/…)
2、Session 的有效范围 比 request 大(1个Session对应同一client的多次请求)
Tomcat会为每个拥有session的浏览器设置一个特别的Cookie存储在浏览器中,称作会话Cookie。会话Cookie的名字固定叫做“JSESSIONID”, Cookie的值就是该浏览器对应的session ID号,其生命周期为-1(30分钟)。
当调用getSession(true)时,他首先检查用户的会话Cookie是否存在
如果不存在:为其创建新的session对象,将session对象的ID保存到新建的会话Cookie中,并将会话cookie送回浏览器 。
如果存在:则按照会话Cookie 的值找到对应的session对象返回。
这是我们运行上面的代码在浏览器Cookie中找到的会话Cookie:
package com.xx.session;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Servlet implementation class GetSessionServelt
*/
public class GetSessionServelt extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public GetSessionServelt() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// HttpSession getSession()
// 返回关于该请求的当前会话。或者若该请求没有会话则就创建一个。
// HttpSession getSession(boolean create)
// 返回有关本请求的当前HttpSession,或者若该请求没有会话,且“创建”属性为真,则就创建一个。
// 1、拿到session对象
HttpSession session = request.getSession(true);
// 2.获取session的id
String id = session.getId();
response.getWriter().println("session_id:" + id);
// 3.判断session的新旧
boolean isNew = session.isNew();
response.getWriter().println("isNew:" + isNew);
// 4.获取会话创建的时间
long time = session.getCreationTime();
response.getWriter().println("createtime=" + time);
// 5.设置最大有效时间,以秒为单位
session.setMaxInactiveInterval(200);
// 6.session失效
// session.invalidate();
// 7.浏览器最后一次请求的时间
long lastAccessedTime = session.getLastAccessedTime();
response.getWriter().println("lastAccessedTime=" + lastAccessedTime);
// 8.session也是域对象
session.setAttribute("name", "aSession");
String name = (String) session.getAttribute("name");
session.removeAttribute("name");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
1、显示当前登录用户信息
思路:登录成功后,将登陆信息 存储到用户的session对象中
步骤1:LoginAction中的成功分支中(跳转前),往session中存数据:
// 往session中,设置命名属性
HttpSession session = request.getSession(true);
session.setAttribute("LoginUser", user);
步骤2:在需要显示当前用户信息的界面中,从session中取数据
// 从session中,获取命名属性
HttpSession session = request.getSession(true);
Object obj = session.getAttribute("LoginUser");
User u = (User)obj;
out.println("当前用户:"+u.getUsername()+" 退出
");
注意:显示当前登录用户信息的代码,可以提取成公共页面,然后使用include包含。
2、强制用户登录
思路:获取并判断session对象中是否存储了用户的登陆信息,如果保存了则说明该用户已经登陆过;否则:说明该用户没有登陆,就强制回到登录页面。
在需要强制登录控制的代码的最上方添加:
// 获取session中的命名属性
HttpSession session = reqeust.getSession(true);
Object obj = session.getAttribute("LoginUser");
// 判断是否登录
if (obj == null){
response.sendRedirect("/xxx/login.html");
return;
}
3、安全退出
思路:删除session中存储的用户登陆信息,并且让session失效。
// 从session中,移除命名属性
HttpSession session = request.getSession(true);
session.removeAttribute("LoginUser");
// 让session失效
session.invalidate();
// 跳转到login
response.sendRedirect("/xxx/login.html");
在Chrome中禁止使用Cookie:
如果client的Cookie被禁用,则每次请求server,都会重新创建新的session对象。
怎么解决:
// 获取session
HttpSession session = request.getSession();
response.getWriter().println(session.getId()+"\t"+session.isNew());
// NoCookieSetSessionServlet是你服务端响应请求的servlet的类名
String url = "NoCookieSetSessionServlet";
// 对url进行重写
String encodeURL = response.encodeURL(url);
// encodeURL方法作用:对原URL进行编码,检查client的cookie是否被禁用,如果被禁则自动在原URL后,添加“;jsessionid=xxxxxx”,实现会话。
response.setContentType("text/html;charset=utf-8");
response.getWriter().println("RequestSessionServlet");