文章目录


会话技术


  1. 会话:一次会话中包含多次请求和响应
    • 一次会话:浏览器第一次给服务器发送请求,会话建立,直到有一方断开为止
  2. 功能:
    • 在一次会话范围内,共享数据。
      由于Http是无状态的,每次请求之间是相互独立的。若要在多次请求之间,进行数据共享,就只能使用会话技术
  3. 实现方式:

    1. 客户端会话技术:Cookie(将数据存在客户端)
    2. 服务端会话技术:Session(将数据存在服务端)


Cookie

使用步骤

  1. 服务端创建Cookie,绑定数据
    ​Cookie cookie = new Cookie("name","yogurt"); ​
  2. 发送Cookie对象
    ​response.addCookie(cookie); ​
  3. 客户端获取Cookie,拿到数据
    ​Cookie[] cookies = request.getCookies(); ​

实现原理

服务端往客户端发送Cookie时,会在response的响应头上设置

​set-cookie:name=yogurt​

浏览器看到这个响应头时,会把这个数据保存在浏览器端

第二次请求时,会把这个cookie放在请求头上

​cookie:name=yogurt​

这是Http协议和浏览器自动做的

Cookie的细节

  • 一次是否能发送多个Cookie?
    可以发送多个Cookie,使用response调用多次addCookie方法即可,多个cookie在response的响应头时设置多个字符串键值对,以逗号隔开
  • Cookie在浏览器中保存多长时间?

    • 默认情况下,在浏览器关闭时,Cookie数据被清除
    • 可以设置cookie生命周期,让其持久化存储setMaxAge(int seconds)

      1. 参数为正数:将cookie写到硬盘文件中,参数为cookie存货时间
      2. 参数为负数:默认值
      3. 参数为零:表示删除cookie


  • Cookie能不能存中文?

    1. tomcat 8之前,cookie不能存储中文
      -> 解决方案:将中文数据转码 , 一般采用URL编码
    2. tomcat 8以后,cookie能存储中文,但对特殊字符还是不支持,比如空格,建议使用URL编码存储,读取时用URL解码。(空格经过URL编码后会变成+)


Cookie的共享问题?

  1. 假设在同一个tomcat服务器中,部署了多个web项目,那么这些web项目中的cookie能不能共享?

    • 默认情况下,多个web项目之间的cookie不能共享
    • ​setPath(String path)​​ ,默认情况下,会调用该方法,设置当前项目的虚拟目录
    • 如果要共享,可以将path设置为/

  2. ​ Cookie cookie = new Cookie("n","c"); cookie.setPath("/");``` ​
  3. 那么在不同tomcat服务器之间的cookie,如何共享?

    1. setDomain(String path) : 如果设置一级域名相同,那么多个服务器之间cookie可以共享。
    2. 比如​​setDomain(".baidu.com")​​,那么​​tieba.baidu.com​​和​​news.baidu.com​​之间也能共享cookie


Cookie的特点和作用

  1. 特点

    1. cookie存储在浏览器端(容易被篡改,不怎么安全)
    2. 浏览器对单个cookie的大小有限制(一般是4kb),对同一个域名下的cookie总数量也有限制(一般是20个以内)

  2. 作用

    1. cookie一般用来存储少量的,不太敏感的数据
    2. 在不登陆的情况下,完成服务器对客户端的身份识别


Cookie使用案例:记录上一次的登录时间

  1. 访问一个Servlet,若是第一次访问,则输出,欢迎访问
  2. 若不是第一次访问,则显示,你好,欢迎回来,你上次登录的时间是:${上次访问时间}
  3. 代码实现

@WebServlet("/loginDemo")
public class ShowLastTimeServlet extends HttpServlet {

private static final String lastTimeLogin = "lastTimeLogin";

private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8;");
Cookie[] cookies = request.getCookies();
PrintWriter writer = response.getWriter();
boolean firstLogin = true;
String lastLoginTimeStr = "";
if (cookies == null || cookies.length == 0){
firstLogin = true;
}else {
for (Cookie cookie : cookies){
String cookieName = cookie.getName();
String cookieValue = cookie.getValue();
cookieValue = URLDecoder.decode(cookieValue,"utf-8");
if (lastTimeLogin.equals(cookieName)){
lastLoginTimeStr = cookieValue;
firstLogin = false;
break;
}
}
}
String nowStr = timeFormatter.format(LocalDateTime.now());
/** 对空格进行特殊处理,使用URL编解码 **/
nowStr = URLEncoder.encode(nowStr,"utf-8");
Cookie newCookie = new Cookie(lastTimeLogin,nowStr);
newCookie.setMaxAge(60 * 60 * 24 * 30);
response.addCookie(newCookie);
if (firstLogin){
writer.write("<h1>你好,欢迎访问!</h1>");
}else {
writer.write("<h1>你好,欢迎回来!你上次的登陆时间是:"+ lastLoginTimeStr +"</h1>");
}

}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}

Session

原理

session是依赖于cookie的。可以通过​​request.getSession()​​​来获取session,若是第一次,则会创建一个session。第一次响应时,会通过在响应头上添加​​set-cookie:JSESSIONID=xxxx​​​。随后在浏览器发起的请求,会带上这个cookie,表现为在请求头上添加​​cookie:JSESSIONID=xxxx​​,然后服务器根据这个id,找到自己内存中的session对象。

细节

  1. 客户端关闭,服务器不关闭,两次获取的session是否为同一个?
    => 默认情况下,不是同一个。若希望是同一个,则可以手动创建一个cookie,其name为JSESSIONID,并用​​setMaxAge()​​方法设置其存活时间
  2. 客户端不关闭,服务器关闭,两次获取到的session是否为同一个?
    => 肯定不是同一个,但在有些场景下需要确保数据不丢失。Tomcat自带有session的钝化和活化策略。
    钝化:在服务器关闭之前,将session序列化,存储到服务器的磁盘中(tomcat的work工作目录下)
    活化:在服务器启动时,从磁盘中反序列化session文件,创建session对象,加载到内存中
    session的钝化和活化是Tomcat默认支持的。需要在Tomcat中启动,才有效。如果在IDEA中启动项目,则只会钝化session,但重启后因为会删除work工作目录,故无法活化session
  3. session失效时长?
    Session在服务器关闭会被销毁;
    调用Session的invalidate也会销毁;
    session默认的失效时长是30分钟。
    Tomcat的web.xml中有对session的配置:​​<session-config>​

特点

  1. 用于存储一次会话,多次请求的数据,存在服务端
  2. session可以存任意类型,任意大小

Session使用案例:校验验证码

  1. 调用servlet生成验证码时,将生成的验证码设置到session中
  2. 用户点击登录后,接收到第二次请求,将其所输入的验证码和session中的做比对,正确则验证通过
  3. 代码实现
    html页面

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>带验证码的登陆案例</title>

<script>
window.onload = function () {
var img = document.getElementById("checkImage");
img.onclick = function () {
var date = new Date().getTime();
img.src = "/checkImage?" + date;
}
}
</script>
</head>
<body>

<form action="/login" method="post">
<table>
<tr>
<td>用户名</td>
<td><input type="text" placeholder="username" name="username"/></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" placeholder="password" name="password" /></td>
</tr>
<tr>
<td>验证码</td>
<td><input type="text" placeholder="请输入验证码" name="checkCode" /></td>
</tr>
<tr>
<td colspan="2"><img src="/checkImage" id="checkImage" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交" /></td>
</tr>
</table>
</form>
</body>
</html>

Servlet代码

@WebServlet(urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 先判断验证码是否正确
// 再验证用户名和密码
String checkCode = request.getParameter("checkCode");
String username = request.getParameter("username");
String password = request.getParameter("password");
response.setContentType("text/html;charset=utf-8;");

String rightCheckCode = (String)request.getSession().getAttribute("checkCode");
String errorMsg = "";
if (checkCode != null && checkCode.equals(rightCheckCode)){
if ("yogurt".equals(username) && "123456".equals(password)){
request.getSession().setAttribute("username","yogurt");
response.sendRedirect("/success.jsp");
}else {
errorMsg = "用户名和密码不正确";
}
}else {
errorMsg = "验证码不正确";
}

if (!"".equals(errorMsg)){
request.getSession().setAttribute("errorMsg",errorMsg);
response.sendRedirect("/failure.jsp");
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}

生成验证码的Servlet

@WebServlet("/checkImage")
public class ImageServlet 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 {
//1 生成一张图
int width = 100;
int height = 50;
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2 美化图
Graphics g = image.getGraphics();
//2.1 填充背景色
g.setColor(Color.PINK);
g.fillRect(0,0,width,height);
//2.2 画边框
g.setColor(Color.BLUE);
g.drawRect(0,0, width - 1,height - 1);
//2.3 写验证码
String str = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890";
Random random = new Random();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 1; i <= 4 ; i++) {
int index = random.nextInt(str.length());
g.drawString(str.charAt(index) + "",width / 5 * i,30);
stringBuilder.append(str.charAt(index));
}
String checkCode = stringBuilder.toString();
HttpSession session = request.getSession();
session.setAttribute("checkCode",checkCode);
//2.4 画干扰线
g.setColor(Color.GREEN);
//随机生成坐标点
for (int i = 1; i < 5; i++) {
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
//3 输出图片到response流
ImageIO.write(image,"jpg",response.getOutputStream());
}
}

JSP页面

failure.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>错误!<%= session.getAttribute("errorMsg")%></h1>
</body>
</html>

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>

<h1>登录成功 <%= session.getAttribute("username")%> , 欢迎你!</h1>
</body>
</html>

Cookie和session区别

1.存储位置不同

2.类型,大小限制不同

3.安全性