六、Session和Cookie

JavaWeb会话技术

HTTP通信协议的特点:

1.应答式的协议,只能是客户端先发送请求然后服务器作出响应。

2.明文传输,传输的数据是明文,没有做加密之类处理

3.简单快速。HTTP协议相对简单,所以传输较快。

4.无状态,HTTP通信,不会记录客户端的请求信息。默认通信完成,传输断开。

基于无状态这点,如果客户端和服务器需要进行二次会话,且可能需要之前会话的数据这样的需求场景。

所以,WEB技术中,提供了2种解决方案,这两种解决方案就被称之为会话技术。分别是:Session和Cookie。

1.如果一方需要之前沟通的记录,在生活中是怎么处理的?

方案一: 将数据存储起来,需要时进行查看。Session

方案二:重新将之前的记录发送一次。Cookie

2.WEB中的Session技术

WEB中的Session技术,是在服务器创建一个Session容器,可以将数据存储在这个容器,然后将查找到这个容器key,给客户端,那么每次客户端来请求服务器时,可以将这个key带给服务器,服务器就可以根据这个key,找到客户端寄存在服务器的Session(数据容器),然后从session中获取存储的数据。

例如: 在超市里,将物品进行寄存,有一个寄存的号码牌,需要时,可以根据号码牌,拿到寄存的物品。

注意:

1.session是在服务器的内存中,开辟了一存储空间,默认是有效时间是30分钟,若没有主动释放,则30分钟会释放内存。

2.session容器是一个map结构的容器,根据key查找对应的session,若key不对,那么session将找不到(找不到不代表它消失了)。一般关闭浏览器,或者使用新的浏览器,服务器会分配一个新的key给浏览器,此时就找不到之前的那个session了,但是之前的session是在内存中的。

3.客户端从存储session对应的key是使用cookie技术存储的,所以session技术也是依赖cookie技术。

2.1.session使用

session作为一个会话对象,其中可以存储多次请求需要共享的数据。例如:验证码,当前登录用户都可以存在session。所以首先要有session容器。

在调试时发现,当第一次访问jsp页面时,会有Cookie,且Cookie中存在JSESSIONID的值,但是访问servlet的时候没有,因为JSP默认会创建session对象(session是JSP的内置对象之一),但是session是不会主动创建session对象的。所以在servlet中使用session,首先要创建一个session对象,而session与客户端的请求关联的,因为session表示会话,会话包含多个请求,所以需要根据请求对象,找到关联的session对象。


session示例.png
session示例2.png

以上图片,浏览器中Cookie中JSESSIONID对应的值,与服务器中产生的sesion对象的ID值一致,说明服务器将自己创建的session对象的ID值给了浏览器。且使用的是Cookie。所以session是依赖Cookie,因为需要使用Cookie存储自己的唯一标识,便于浏览器找到对应的Session.

package com.sxt.controller;

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;
/**
 * @ClassName: SessionController 
 * @Description: 演示session的API
 * @author: Mr.T
 * @date: 2020年2月14日 上午11:36:30
 */
public class SessionController extends HttpServlet {

    private static final long serialVersionUID = -6539113475281947079L;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("service方法执行了");
        // getSession() : 返回与当前请求关联的session 如果没有则创建一个新的session
        // getSession(create) : 返回当前关联的session对象, 如果create 为true,当前session不存在时创建一个,为false则返回null
        //一般都是使用  getSession() 
        HttpSession session = req.getSession();
        System.out.println("session的ID:"+session.getId());
        //resp.getWriter().println("Hello Session");
        //session中核心方法
        //session 作为作用域  其核心作用是数据管理
        //setAttribute(name, value) :向session 作用域中存储数据
        //getAttribute(name) : 从session作用域中获取数据
        //getAttributeNames() : 从session作用域中获取所有存储数据的name值
        //session生命周期方法
        //setMaxInactiveInterval(long) : 设置session 有效时间  单位是秒  
        //如果设置为0  或者 负数,那么session 将永远不会失效  不建议  因为session占服务器内存
        // tomcat 服务器 默认是 30分钟
        //session.setMaxInactiveInterval(interval);
        //invalidate() : 是这个session不关联任何对象,即取消session和request的关联
        //这样当前request 就无法找到这个session
        //session.invalidate();
        
    }   
    
}

2.2session目前的不足

session是存储在服务器的内存中,所以session只支持单应用,而不支持负载形式的应用架构。

所以session只支持单应用的架构,只支持只有一台服务器的架构。

session的不足.png

2.3.使用session记录当前用户

一般在开发中,会将当前登录用户,存储在内存中,便于其他地方使用。而在web程序,会将当前的登录用户,存储在session中。然后,在其他页面或者servlet中,若要使用当前用户信息,则可以直接从session中获取,不必根据用户名和密码重新查询,只需要登录一次即可。

代码:

package com.sxt.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sxt.pojo.User;
/**
 * @ClassName: UserController 
 * @Description: 用户控制类
 * @author: Mr.T
 * @date: 2020年2月14日 下午2:40:11
 */
public class UserController extends HttpServlet {
    
    private static final long serialVersionUID = 7723086583204133716L;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userName = req.getParameter("userName");
        String password = req.getParameter("password");
        //根据用户名和密码查询用户
        //假设  用户名和密码 值都是 admin 当做登录成功
        if("admin".equals(userName) && "admin".equals(password)) {
            //将用户信息存在session中
            User user = new User(1, "admin", "password","韩梅梅");
            //放入session  便于在当前会话中  任何页面或者servlet 都可以直接拿到当前登录用户信息
            req.getSession().setAttribute("user", user);
        }
        //去登录成功页面
        resp.sendRedirect("success.jsp");
    }       
}

package com.sxt.controller;

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;

import com.sxt.pojo.User;
/**
 * @ClassName: TestController 
 * @Description: 用于测试 可以从session中获取当前登录用户
 * @author: Mr.T
 * @date: 2020年2月14日 下午2:47:58
 */
public class TestController extends HttpServlet {

    private static final long serialVersionUID = -5123674255870472885L;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        User user = (User) session.getAttribute("user");
        System.out.println("当前登录的用户是:"+user.getRealName());
    }
    

}

package com.sxt.pojo;

public class User {
    
    private Integer userId;
    
    private String userName;
    
    private String password;
    
    private String realName;
    
    
    public User() {}

    public User(Integer userId, String userName, String password, String realName) {
        super();
        this.userId = userId;
        this.userName = userName;
        this.password = password;
        this.realName = realName;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }
    
    
    

}

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




登录页面


用户名:

用户名:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




Insert title here



欢迎:${user.realName}


2.4.使用session存储验证码

一般在web网页上看到的图片,其实是服务器将图片数据使用字节流,输出给浏览器,然后浏览器拿到数据后,将数据解析成图片,渲染在页面。

那么,在页面上看到的验证码图片,其实就是服务器生成的一张图片,然后将图片使用字节流发送给了,浏览器。然后浏览器解析。并且,在处理登录请求的servlet时,拿到用户输入的验证码和之前产生的验证码要进行比对。

那么此时,就是在登录的servlet,要使用造验证码的servlet产生的数据,但是造验证码servlet程序已经执行完成了,内存已经释放了。之前造的验证码字符串已经消失了,无法进行比对。基于这样的情况,所以一般是将产生验证码的字符串,存储在session中,这样在登录请求的servlet中,也可以拿到之前产生的验证码,从而进行比对。

产生验证码

package com.sxt.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * @ClassName: CheckCodeController 
 * @Description: 产生验证码的servlet
 * @author: Mr.T
 * @date: 2020年2月14日 下午3:27:44
 */
public class CheckCodeController  extends HttpServlet {

    private static final long serialVersionUID = -1597171426922505140L;
    /**
     * 验证码池  
     */
    static String[] codes = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d"};
    
    
    /**
     * 验证码的本质 其实就是 一个有随机字符串的图片
     * 服务器,将图片转化为字节流输出给浏览器
     * 浏览器解析流数据,将图片渲染在页面
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //要使用Java程序  造一个含有随机字符串的图片
        //1. 创建一个背景图  画布
        BufferedImage image =  new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB);
        //2. 获取一个跟画布关联的画笔
        Graphics graphics = image.getGraphics();
        //3.使用画笔绘制信息
        //为画笔设置颜色
        graphics.setColor(Color.WHITE);
        //绘制一个填充的矩形  将默认黑色的画布 涂成白色
        graphics.fillRect(0, 0, 200, 100);
        //1. 获取验证码字符串   4个字符长度的验证码
        String code = getCode(4);
        //将验证码 放入session 便于在其他地方使用
        req.getSession().setAttribute("code", code);
        // 设置画笔的字体 宋体 加粗 倾斜 35磅值
        Font font = new Font("宋体", Font.BOLD | Font.ITALIC, 60);
        graphics.setFont(font);
        //2.根据循环将字符 绘制到图片上
        for (int i = 0; i < code.length(); i++) {
            //随机改变画笔颜色
            Color c = getColor();
            graphics.setColor(c);
            //获取具体的字符
            String str = code.charAt(i)+"";
            //让文字的横坐标值变大,从而向右侧移动
            int x = 20 + i*40;
            graphics.drawString(str, x,65);
        }
        
        
        
        ImageIO.write(image, "jpg", resp.getOutputStream());
    }
    
    
    /**
     * @Title: getColor
     * @author: Mr.T   
     * @date: 2020年2月14日 下午3:54:56 
     * @Description: 随机产生颜色
     * @return
     * @return: Color
     */
    private Color getColor() {
        //因为RGB颜色 是由  0-255,0-255,0-255 的数值组成
        Random random = new Random();
        int r = random.nextInt(255);
        int g = random.nextInt(255);
        int b = random.nextInt(255);
        return new Color(r, g, b);
    }
    /**
     * @Title: getCode
     * @author: Mr.T   
     * @date: 2020年2月14日 下午3:57:55 
     * @Description: 产生随机字符串
     * @param count 验证码的个数
     * @return
     * @return: String
     */
    private String getCode(int count) {
        String code = "";
        Random random = new Random();
        for (int i = 0; i < count; i++) {
            int index = random.nextInt(codes.length);
            code = code + codes[index];
        }
        return code;
    }
    
    /**
     * @Title: method
     * @author: Mr.T   
     * @date: 2020年2月14日 下午4:03:38 
     * @Description: 绘制验证码相关API的演示
     * @return: void
     */
    private void method(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 要使用Java程序 造一个含有随机字符串的图片
        // 1. 创建一个背景图 画布
        BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB);
        // 2. 获取一个跟画布关联的画笔
        Graphics graphics = image.getGraphics();
        // 3.使用画笔绘制信息
        // 为画笔设置颜色
        graphics.setColor(Color.WHITE);
        // 绘制一个填充的矩形 将默认黑色的画布 涂成白色
        graphics.fillRect(0, 0, 200, 100);
        // 可以在白色画布上绘制文字 注意 此时画笔是白色 绘制信息也是白色 所以需要改变画笔的颜色
        // 画笔设置为红色
        graphics.setColor(Color.RED);
        // 设置画笔的字体 宋体 加粗 倾斜 35磅值
        Font font = new Font("宋体", Font.BOLD | Font.ITALIC, 30);
        graphics.setFont(font);
        graphics.drawString("情人节快乐", 10, 55);
        // 绘制干扰线 2点 连起来 就是一条线
        graphics.drawLine(0, 0, 200, 100);
        // 使用输出流 将图片数据输出给浏览器
        ImageIO.write(image, "jpg", resp.getOutputStream());
    }
    
}

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




登录页面


用户名:

用户名:

用户名:

使用Hutool生成验证码:

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         *  width  : 图片的宽度
         *  height : 图片高度
         * codeCount : 验证码的个数
         * circleCount : 干扰元素个数
         */
        LineCaptcha checkObj = CaptchaUtil.createLineCaptcha(200, 100, 4, 100);
        //获取具体的验证码字符串
        String code = checkObj.getCode();
        //将生成的验证码 放入session
        req.getSession().setAttribute("code", code);
        //将验证码输出到页面
        checkObj.write(resp.getOutputStream());
    }

校验验证码:

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取用户输入的验证码
        String checkCode = req.getParameter("checkCode");
        //从session中获取产生时存储的验证码
        Object code = req.getSession().getAttribute("code") ;
        //对验证码进行对比
        if(checkCode == null || !checkCode.equals(code.toString())) {
            System.out.println("验证码错误!");
            resp.sendRedirect("login.jsp");
            return;
        }
        String userName = req.getParameter("userName");
        String password = req.getParameter("password");
        //根据用户名和密码查询用户
        //假设  用户名和密码 值都是 admin 当做登录成功
        if("admin".equals(userName) && "admin".equals(password)) {
            //将用户信息存在session中
            User user = new User(1, "admin", "password","韩梅梅");
            //放入session  便于在当前会话中  任何页面或者servlet 都可以直接拿到当前登录用户信息
            req.getSession().setAttribute("user", user);
        }
        //去登录成功页面
        resp.sendRedirect("success.jsp");
    }

3.WEB中的Cookie技术

在WEB中,Cookie技术是将需要的数据,由服务器写入(响应)到浏览器中(内存中,一个文件中),然后,浏览器接下来的请求,都会将这些数据通过请求头,带给服务器。服务器可以通过解析请求头,从而解析这些数据。这种技术方案就被称之为Cookie.

注意:

  1. Cookie的存储2种方式,一种是内存中一种磁盘文件中,这两种方式是Cookie生命周期的体现。默认Cookie的生命周期,是在内存中,基本是一次会话。
  2. Cookie是由服务器,通过响应对象,将数据通知给客户端,让客户端在本地进行一定的存储,且下次来请求带过来。

3.1.Cookie的使用

  1. 创建Cookie

    Cookie cookie1 = new Cookie("name","value"); // cookie是map结构

  2. 通过response对象,将创建的cookie给浏览器

    resp.addCookie(cookie1); //通过响应对象,设置set-cookie的响应头,浏览器拿到这个响应头后 ,会将响应头找中的数据保存到浏览器的内存中或者本地

  3. 以后的请求,浏览器会将cookie信息带给服务器,服务器可以从请求头中,解析cookie信息。

代码示例:

/**
 * @ClassName: CookieController 
 * @Description: 用于演示Cookie相关的API
 * @author: Mr.T
 * @date: 2020年2月16日 上午10:47:39
 */
public class CookieController extends HttpServlet {

    private static final long serialVersionUID = 2285648241402004645L;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //cookie是由服务器  告诉客户端 要存储哪些数据 
        //cookie是由服务器创建  然后 服务器将这个cookie 给客户端   客户端将cookie数据进行存储
        //之后的请求,都会将cookie数据带给服务器
        //1. 创建Cookie --- cookie数据结构  map接口
        Cookie cookie1 = new Cookie("cookieName", "cookieValue");
        //设置cookie的有效期 单位是秒   如果设置为 0 则表示要删除这个cookie
        //如果设置了有效期,浏览器会将cookie存储在自己本地的文件中,如果不清除,且没有过有效期,那么cookie就有效
        //之后请求,会将数据带给服务器
        cookie1.setMaxAge(60*2);//2分钟
        //设置cookie是否允许浏览器进行操作   由于cookie是存在浏览器中,浏览器可以通过js对cookie进行数据操作
        //servlet 3.0 后   setHttpOnly(true) : 只有HTTP可以操作Cookie 浏览器无法操作Cookie
        cookie1.setHttpOnly(true);
        //注意,cookie 默认只对当前项目生效   有效路径  当前项目   域名  项目名
        //但是实际中,存在子域名,当需要多个子域名cookie也生效,则需要设置这个属性 
        //cookie1.setDomain(pattern);
        //注意,默认只对项目及项目的子目录生效。限制生效的URI,通过setPath进行设置 
        //cookie1.setPath(uri);
        
        //2. 通过响应对象  将cookie 给客户端
        resp.addCookie(cookie1);
        resp.getWriter().print("Hello  Cookie");
    }
}
@Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //cookie数据 是浏览器 通过请求头的信息  将数据传给服务器的,所以解析cookie 要从请求对象中解析
        // 获取所有的cookie
        Cookie[] cookies = req.getCookies();
        //判断cookie是否存在
        if(cookies != null) {
            //只能通过遍历的方式进行判断   遍历所有cookie 获取每个cookie的name值   对比name 从而拿到自己需要的cookie对象
            for (Cookie cookie : cookies) {
                //获取cookie的name
                String name = cookie.getName();
                //获取cookie的Value值
                String value = cookie.getValue();
                if(name.equals("cookieName")) {
                    System.out.println("cookieName 对应的值:"+value);
                }
            }
        }
    }

cookie01.png
cookie02.png

3.2.Cookie的不足

Cookie是将数据存储在浏览器,然后每次浏览器自动会将Cookie数据放在请求头中,传递给无服务器。

1.安全性相对较差,可以在传输的过程中,修改Cookie中数据。

2.无法与其它终端共享数据。例如:用户在PC端进行了操作,数据存在Cookie中,用户在移动端是看不到用户在PC端的操作的。

3.Cookie中存储的数据有限.

3.3.Cookie的场景

1.早期时,使用Cookie做购物车。将用户的购买的商品放入Cookie中。但是在现在,使用Cookie作为购物车,无法满足需求,因为没有办法在其它终端共享数据。所以,为了共享数据,会将数据存在服务器数据库中,便于在任何平台都能看到用户的购物车,前提是用户登录了。所以,如果用户没有登陆,会将用户购物车数据存储在Cookie中,但是,当用户登录后,将Cookie中的数据保存到数据库中。

2.用户的浏览记录。一般会将用户的浏览记录放入Cookie。例如: 浏览器的访问记录.

3.使用Cookie记录用户的登录凭证,让用户免密登录。记住密码。

3.5.使用Cookie记住密码

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




登录页面



用户名

密码:

记住密码:

package com.sxt.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @ClassName: UserController 
 * @Description: @WebServlet 就是代替 web.xml中的servlet 配置
 *              "/user.do"  就是xml中 url-pattern的值
 * @author: Mr.T
 * @date: 2020年2月16日 下午2:34:42
 */
@WebServlet("/user.do")
public class UserController extends HttpServlet {

    /**
     * @fieldName: serialVersionUID
     * @fieldType: long
     * @Description: TODO
     */
    private static final long serialVersionUID = 7723086583204133716L;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取用户名
        String userName = req.getParameter("userName");
        //获取密码
        String password = req.getParameter("password");
        //是否记住密码
        String remeber = req.getParameter("remeber");
        /**
         * 如果记住密码  可以将账号密码信息放入cookie  下次登录 直接从cookie中取  不用再输入账号面
         * 
         */
        if("1".equals(remeber)) {//表示记住密码
            //将账号放入cookie
            Cookie userNameCookie = new Cookie("userName", userName);
            //由于 cookie 默认是浏览器关闭就会消失,设置cookie的有效期
            userNameCookie.setMaxAge(60*60*24*7);//1个星期
            //通过resp 将cookie给浏览器
            resp.addCookie(userNameCookie);
            
            //将密码 放入cookie
            Cookie passwordCookie = new Cookie("password", password);
            //由于 cookie 默认是浏览器关闭就会消失,设置cookie的有效期
            passwordCookie.setMaxAge(60*60*24*7);//1个星期
            //通过resp 将cookie给浏览器
            resp.addCookie(passwordCookie);
        }
        resp.getWriter().print("登录成功"); 
    }
}

你可能感兴趣的:(六、Session和Cookie)