zl程序教程

您现在的位置是:首页 >  其他

当前栏目

会话技术知识点整理(Cookie和Session)

2023-03-14 22:55:03 时间

会话技术

会话: 一次会话中包含多次请求和响应

一次会话: 浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止

功能

在一次会话的范围内的多次请求间共享数据

方式

  1. 客户端会话技术: cookie 2.服务器端会话技术; session

Cookie

概念: 客户端会话技术,将数据保存到客户端

基础使用步骤

修改servlet模板代码,方便编程

第一步:

第二步:

第三步:

第四步:


代码演示

CookieServlet1: ---->发送Cookie方

@WebServlet("/CookieServlet1")
public class CookieServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     //1.创建Cookie对象
        Cookie c=new Cookie("msg","hello");
        //2.发送Cookie
        response.addCookie(c);
    }

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

CookieServlet1:接收Cookie方

@WebServlet("/CookieServlet")
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       //获取Cookie
        Cookie[] cs = request.getCookies();
        if(cs!=null)
        {
            for(Cookie c:cs)
            {
                String name = c.getName();
                String value = c.getValue();
                System.out.println(name+" "+value);
            }
        }
    }

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

Cookie实现原理

基于响应头set_cookie和请求头cookie实现

因为cookie是基于响应头set_cookie实现的,因此我们可以直接通过设置响应头的方式,完成cookie的发送

一旦发送给浏览器某个cookie对象后,那么在一次会话间,每次访问这个网站时,请求头中都会带上这个cookie对象

第一次访问服务器,响应头会返回 set-Cookie:

第二次访问,会带着上次请求返回的 Cookie 访问:

也需要注意,第一次 访问 Cookie1,接收到返回的 set-Cookie,第二次不是只有访问 cookie2 ,才能在响应头里看到Cookie,第二次访问哪个都会被携带,这里只是,我们在 Cookie2 这个页面设置了接收 Cookie 的数组,会被打印出来而已。


Cookie细节

一次发送多个Cookie

创建多个Cookie对象,使用response调用多次addcookie方法发送cookie即可

     //1.创建Cookie对象
        Cookie c=new Cookie("msg","hello");
        Cookie c1=new Cookie("msg","hello");
        //2.发送Cookie
        response.addCookie(c); 
        response.addCookie(c1);

Cookie在浏览器中保存的时间

默认情况下,浏览器被关闭后,cookie数据被销毁

cookie默认在会话期间有效,只要浏览器不关闭,cookie就存在,即cookie存在于浏览器的进程之中

cookie的修改和删除都是同名cookie覆盖

Cookie的持久化存储

setMaxAge(int seconds)--->秒
  1. 正数: 将Cookie数据写到硬盘文件中,持久化存储. Cookie存活时间
  2. 负数:默认值,会话cookie,跟随浏览器的进程
  3. 0:删除Cookie信息

Cookie能不能存储中文数据

  • tomcat 8版本之前,Cookie中不能直接存储中文数据 需要将中文数据转码,一般采用URL编码(%E3)
  • tomcat 8版本之后,Cookie中支持中文数据 特殊字符还是不支持,建议使用URL编码存储,URL编码解析,例如空格

Cookie共享问题

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

  • 默认情况下cookie不能共享
setPath(String path): 设置Cookie的共享范围. 默认情况下,设置为当前的虚拟目录

如果要共享,可以将Cookie设置为"/"

/----->当前项目的根路径—>localhost/

注意: 以上方法是用来解决同一个服务器下,多个web项目的Cookie共享问题


2 不同tomcat服务器间Cookie共享问题 ?

setDomain(String path):如果设置一级域名相同,那么多个服务器之间Cookie可以共享

例如:

setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中的Cookie可以共享

Cookie的特点和作用

特点:

  1. Cookie存储数据在客户端浏览器
  2. 浏览器对于单个Cookie的大小由限制(4kb),以及对同一域名下的总Cookie数量也有限制(20)

作用:

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

案例: 记住上一次访问时间

注意: cookie不支持特殊字符,因此需要采用URL编码存储,用URL解码来解析

代码加注释:

package com.example.Cookie1;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;

@WebServlet("/CookieServlet")
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置响应消息体的数据格式以及编码
        response.setContentType("text/html;charset=utf-8");
        //判断是否有cookie的值为lastTime
        boolean flag=false;
        //获取所有Cookie
        Cookie[] cookies = request.getCookies();
        //遍历Cookie数组
        if(cookies!=null&&cookies.length>0)
        {
            for(Cookie c:cookies)
            {
               //获取cookie的名称
                String name = c.getName();
                //判断名称是否是lastTime
                if("lastTime".equals(name))
                {
                    //有该cookie,说明不是第一次访问
                    flag=true;//有lastTime的cookie
                    //响应数据
                    //获取cookie的value---->时间
                    String value = c.getValue();
                    System.out.println("解码前:"+value);
                    //URL: 解码
                    value= URLDecoder.decode(value, "utf-8");
                    System.out.println("解码后:"+value);

                    response.getWriter().write("<h1>欢迎回来,您上一次访问的时间为:"+value+"</h1>");

                    //设置Cookie的value
                    //获取当前时间的字符串,重新设置cookie的值,重新发送cookie
                    Date date = new Date();
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
                    String format = simpleDateFormat.format(date);
                    
                    //URL编码
                    System.out.println("编码前:  "+format);
                   format=URLEncoder.encode(format,"utf-8");//
                    System.out.println("编码后:"+format);
                    //设置cookie的值
                    c.setValue(format);
                    //设置cookie的存活时间---一个月
                    c.setMaxAge(60*60*24*30);
                    //发送cookie
                    response.addCookie(c);

                       break;
                }
            }
        }
      if(cookies==null||cookies.length==0||flag==false)
        {
           //没有,说明是第一次访问
            //设置Cookie的value
            //获取当前时间的字符串,重新设置cookie的值,重新发送cookie
            Date date = new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
            String format = simpleDateFormat.format(date);
            Cookie c=new Cookie("lastTime",format);
            //URL编码
            System.out.println("编码前:  "+format);
            format=URLEncoder.encode(format,"utf-8");//
            System.out.println("编码后:"+format);
            //设置cookie的值
            c.setValue(format);
            //设置cookie的存活时间---一个月
            c.setMaxAge(60*60*24*30);
            //发送cookie
            response.addCookie(c);
            response.getWriter().write("<h1>欢迎首次访问</h1>");
        }
    }

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

Session

概念

服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。 HttpSession

一次会话的概念

一次会话,什么是一次会话呢?我举个例子:就我们现在上网,打开浏览器浏览网站当时开始到你关闭浏览器,就称之为一次会话;

HttpSession对象—共享数据

1 获取HttpSession对象

 request.getsession()

2 使用HttpSession对象

Object getAttribute(String name)
void setAttribute(String name,Object value)
void removeAttribute(String name)

代码演示:

Demo1Servlet :

@WebServlet("/SessionDemo1")
public class SessionDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         //使用session共享数据

        //1.获取session数据
        HttpSession session = request.getSession();
        //2.存储数据
        session.setAttribute("msg","大忽悠");
    }

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

Demo2Servlet:

@WebServlet("/SessionDemo2")
public class SessionDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         //1.获取session
        HttpSession session = request.getSession();
        //2.获取数据
        Object msg = session.getAttribute("msg");
        System.out.println(msg);
    }

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

Session原理

如果第一次获取session,此时没有cookie,会创建一个session,然后将该session的id响应给浏览器,浏览器创建一个cookie来保存这个id

如果第二次获取session,此时有cookie,便会去找cookie要来id,这样就可以在服务器端找到对应的session对象

如果第二次获取session时,没了cookie,便会执行第一次获取session的步骤

如果第一次访问index页面,因为session时内置对象,因此会创建一个session对象

Session的实现是依赖与Cookie的


当客户端关闭后,服务器不关闭,两次获取session是否为同一个

如果没有设置cookie的持久化保存,那么浏览器关闭后,cookie销毁,那么cookie里面记录的JSESSIONID也随之销毁,下次打开浏览器发现cookie没了,也就无法得到对应的cookie里面记录的JSESSIONID,那么就会新创建一个cookie,保存新创建的session的id

默认情况下不是,因为客户端关闭后,一次会话就结束了.

如果需要相同,则可以创建Cookie,键为JSESSIONID,让Cookie持久化保存

代码演示:

         //1.获取session
        HttpSession session = request.getSession();
        Cookie c=new Cookie("JSESSIONID",session.getId());
        c.setMaxAge(60*60);
        response.addCookie(c);
        //2.获取数据
        Object msg = session.getAttribute("msg");
        System.out.println(msg);

客户端不关闭后,服务器关闭,两次获取session是否为同一个

不是同一个,但是要确保数据不丢失

1.session钝化: 在服务器正常关闭之前,将session对象系列化到硬盘上

2.session活化: 在服务器启动后,将session文件转化为内存中的session对象即可

tomcat服务器会自动完成钝化和活化过程,但是IDEA不会,因此一般将项目直接放到tmocat的webapps目录下


Session被销毁时机

Session有默认的失效时间,服务器关闭session不会被销毁,并且可以有多个session同时存在的请求,因为浏览器每次关闭,再打开时,如果需要session对象,发现id没了,会新创建一个cookie保存新sesssion的id。那么之前cookie对象里面保存的id消失了,也就找不回原来的session,但是他还活着

session对象调用invalidate()来销毁自己

session默认失效时间为30分钟

我们可以对默认失效时间进行修改:

web.xml配置文件目录,大概500-600多行:


session特点

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

session与cookie的区别

  1. session存储数据在服务器端,cookie在客户端
  2. session没有数据大小限制,而cookie有
  3. session数据安全,cookie相对于不安全

验证码案例

1.案例需求

2.案例分析

在访问浏览器时,会有两个请求,因为验证码图片试一次单独的请求

完整代码

需要导入的jar包:

配置文件:

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test1
username=root
password=xxxx
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>战地六游戏官网</title>

    <script>
        window.onload=function(){
        document.getElementById("img").onclick=function (){
            this.src="/ZhanDi/CheckCodeServlet?time="+new Date().getTime();
        }
        }
    </script>
    <style>
        div{
            color: red;
        }
    </style>
</head>
<body>
    <form action="/ZhanDi/LoginServlet" method="get">
         <table>
             <tr>
                 <th>用户名:</th>
                 <td><input type="text" placeholder="请输入用户名" name="username"></td>
             </tr>

             <tr>
                 <th>密码:</th>
                 <td><input type="password" placeholder="请输入密码" name="password"></td>
             </tr>

             <tr>
                 <th>验证码:</th>
                 <td><input type="text" placeholder="请输入验证码" name="checkcode"></td>
             </tr>

             <tr>
                 <td colspan="2"> <img id="img" src="/ZhanDi/CheckCodeServlet"></td>
             </tr>

             <tr>
                 <td colspan="2"> <input type="submit" value="登录"></td>
             </tr>
         </table>

    </form>
    <div><%=request.getAttribute("yzm_error")==null?"":request.getAttribute("yzm_error")%></div>
    <div><%=request.getAttribute("dl_error")==null?"":request.getAttribute("dl_error")%></div>
</body>
</html>

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>战地1</title>
</head>
<body>
<h1>欢迎回来,<%=request.getSession().getAttribute("user")%></h1><br>
        <img src="/zd.png"/>
</body>
</html>

CheckCode目录下:

1.CheckCodeServlet

package Checkcode;

import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width=150;
        int height=50;
        //1.在内存中创建一个图片----创建一个不带透明色的对象
        BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);

        //2.填充背景色
        Graphics g=image.getGraphics();//画笔对象
        g.setColor(Color.PINK);
        g.fillRect(0,0,width,height);
        //3.画边框
        g.setColor(Color.BLUE);
        g.drawRect(0,0,width-1,height-1);

        //4.随机生成验证码
        String str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random ran=new Random();
        StringBuilder sb=new StringBuilder();
        for(int i=1;i<=4;i++)
        {
            //生成随机角标
            int index=ran.nextInt(str.length());
            //获取字符
            char ch=str.charAt(index);
            //将每个验证码字符加入sb中
            sb.append(ch);
            //写验证码
            g.drawString(ch+" ",width/5*i,height/2);
        }
        //获取验证码字符串
        String checkCode_session= sb.toString();
        //将验证码存入session
        request.getSession().setAttribute("checkCode_session",checkCode_session);
        //7.画干扰线
        //随机生成坐标点
        for(int i=0;i<4;i++)
        {
            int x1=ran.nextInt(width);
            int x2=ran.nextInt(width);
            int y1=ran.nextInt(height);
            int y2=ran.nextInt(height);
            g.setColor(Color.RED);
            g.drawLine(x1,y1,x2,y2);
        }
        //8.将图片输出到页面上----第二个参数是后缀名,第三个参数是一个输出流
        ImageIO.write(image,"jpg",response.getOutputStream());
    }

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

2.loginServlet

package Checkcode;

import mySQL.UserDao;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置request编码----和页面的编码统一
        request.setCharacterEncoding("utf-8");
        //获取参数Map
        Map<String, String[]> parameterMap = request.getParameterMap();
        //创建User对象
        User user=new User();
        //使用BeanUtils工具类的populate方法将map集合封装成一个user对象
        try {
            BeanUtils.populate(user,parameterMap);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
         //判断验证码输入的是否正确
        //1.先获取程序生成的验证码
        HttpSession session = request.getSession();
        String checkCode_session = (String)session.getAttribute("checkCode_session");
        //删除session中存储的验证码,确保验证码是一次性的
        session.removeAttribute("checkCode_session");
        //测试代码
        System.out.println("程序生成的验证码:"+checkCode_session);
        System.out.println("用户输入的验证码:"+user.getCheckcode());
        System.out.println("用户名:"+user.getUsername()+"  "+"密码:"+user.getPassword());
        //验证码忽略大小写
        if(checkCode_session!=null&&checkCode_session.equalsIgnoreCase(user.getCheckcode()))
        {
            System.out.println("验证码正确");
            //验证码正确
            //判断用户名和密码是否一致
            //通过在数据库中查询来判断
            User u=UserDao.check(user);
           if(u!=null&&user.getUsername().equals(u.getUsername())&&user.getPassword().equals(u.getPassword()))
           {
               System.out.println("登录成功");
               //登录成功
               //存储用户信息---两次请求,要把信息存储到session里面
               session.setAttribute("user",user.getUsername());
               //重定向到success.jsp---动态获取虚拟目录
               response.sendRedirect(request.getContextPath()+"/success.jsp");
           }
           else
           {
               System.out.println("登录失败");
               //登录失败
               //存储信息到request
               request.setAttribute("dl_error","用户名或密码错误");
               //转发到登录页面
               request.getRequestDispatcher("/login.jsp").forward(request,response);
           }
        }
        else
        {
            //验证码不一致
            //存储提示信息到request,然后在页面上面显示
            request.setAttribute("yzm_error","验证码错误");
            //转发到登录页面----不需要写虚拟目录
            request.getRequestDispatcher("/login.jsp").forward(request,response);

        }
    }

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

3.user

package Checkcode;

public class User {
    private String username;
    private  String password;
   private  String checkcode;

    public User() {
    }

    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 getCheckcode() {
        return checkcode;
    }

    public void setCheckcode(String checkcode) {
        this.checkcode = checkcode;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + ''' +
                ", password='" + password + ''' +
                ", checkcode='" + checkcode + ''' +
                '}';
    }
}

MySQL:

JDBCUtils:

package mySQL;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
    //1.定义成员变量
    private static DataSource ds;

    static {
        try {
            //1.加载配置文件
            Properties pro=new Properties();
            pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            //2.获取DataSource
            ds= DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    //释放资源
    public static void close(ResultSet res,Statement stmt, Connection conn)
    {
        if(res!=null)
        {
            try {
                res.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if(stmt!=null)
        {
            try {
                stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if(conn!=null)
        {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

    }

    public static void close(Statement stmt, Connection conn)
    {
        close(null,stmt,conn);
    }

    public static  DataSource getDataSource()
    {
        return ds;
    }

}

UserDao

package mySQL;

import Checkcode.User;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

/*
*在mysql数据库中查询相关的用户
* */
public class UserDao
{
    //声明JDBCTemplate对象共用
    static private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
   //查询用户
     static public User check(User loginuser)
    {
        try
        {
            //编写查询的sql语句
            String sql="select* from loginuser where username= ? and password= ?";
            //调用query方法---返回一个查到的user对象---user对象唯一,因此使用queryForObject返回查询到的唯一的user对象
            //该方法必须返回一个结果,否则报错
            User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginuser.getUsername(),loginuser.getPassword());
            System.out.println("数据库查到对应用户");
            return user;
        }
        catch (DataAccessException e)
        {
            //没有查询到
            System.out.println("数据库没有查到对应用户");
            return null;
        }
    }

}

注意:

效果演示:


总结

cookie失效就好像客户(浏览器)把银行卡丢了(cookie失效了),但是银行(服务器端)里面还有对应的账号存在

为了防止弄丢银行卡,因此需要告诫客户好好保存(cookie持久化技术)

session失效就好像银行账号失效了(session失效),虽然客户(浏览器)有卡(cookie没有失效)但是卡已经用不了


令牌机制

java 防止表单的重复提交-令牌机制