Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(六)

简介: Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session

利用Session防止表单重复提交

  • 重复提交的危害:
  • 在投票的网页上不停地提交,实现了刷票的效果。
  • 注册多个用户,不断发帖子,扰乱正常发帖秩序。
  • 首先我们来看一下常见的重复提交。
  • 在处理表单的Servlet中刷新。
  • 后退再提交
  • 网络延迟,多次点击提交按钮
  • 下面的gif是后退再提交,在处理提交请求的Servlet中刷新

image.gif

下面的gif是网络延迟,多次点击提交按钮

1.gif

  • 对于网络延迟造成的多次提交数据给服务器,其实是客户端的问题。于是,我们可以使用javaScript来防止这种情况
  • 要做的事情也非常简单:当用户第一次点击提交按钮时,把数据提交给服务器。当用户再次点击提交按钮时,就不把数据提交给服务器了。
  • 监听用户提交事件。只能让用户提交一次表单!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>表单提交</title>
    <script type="text/javascript">
        //定义一个全局标识量:是否已经提交过表单数据
        var isCommitted = false;
        function doSubmit() {
            //false表示的是没有提交过,于是就可以让表单提交给Servlet
            if(isCommitted==false) {
                isCommitted = true;
                return true;
            }else {
                return false;
            }
        }
    </script>
</head>
<body>
<form action="/ouzicheng/Servlet7" onsubmit="return doSubmit()">
    用户名:<input type="text" name="username">
    <input type="submit" value="提交">
</form>
</body>
</html>

好的,我们来试一下是不是真的可以解决网络延迟所造成的多次提交表单数据,注意鼠标,我已经点击过很多次的了!

2.gif

  • 由于网络延迟造成的多次提交数据给服务器,我们还可以使用javaScript代码这样解决:当我点击过一次提交按钮时,我就把提交的按钮隐藏起来。不能让用户点击了
  • 想要让按钮隐藏起来,也很简单。只要获取到按钮的节点,就可以控制按钮的隐藏或显示了!
 <script type="text/javascript">
        function doSubmit() {
            var button = document.getElementById("button");
            button.disabled = disabled;
            return true;
        }
    </script>

我们再来看一下效果

3.gif

  • 在处理表单的Servlet中刷新后退再提交这两种方式不能只靠客户端来限制了。也就是说javaScript代码无法阻止这两种情况的发生。
  • 于是乎,我们就想得用其他办法来阻止表单数据重复提交了。我们现在学了Session,Session可以用来标识一个用户是否登陆了。Session的原理也说了:不同的用户浏览器会拥有不同的Session。而request和ServletContext为什么就不行呢?request的域对象只能是一次http请求,提交表单数据的时候request域对象的数据取不出来。ServletContext代表整个web应用,如果有几个用户浏览器同时访问,ServletContext域对象的数据会被多次覆盖掉,也就是说域对象的数据就毫无意义了。
  • 可能到这里,我们会想到:在提交数据的时候,存进Session域对象的数据,在处理提交数据的Servlet中判断Session域对象数据????。究竟判断Session什么?判断Session域对象的数据不为null?没用呀,既然已经提交过来了,那肯定不为null。
  • 此时,我们就想到了,在表单中还有一个隐藏域,可以通过隐藏域把数据交给服务器
  • 判断Session域对象的数据和jsp隐藏域提交的数据是否对应
  • 判断隐藏域的数据是否为空【如果为空,就是直接访问表单处理页面的Servlet
  • 判断Session的数据是否为空【servlet判断完是否重复提交,最好能立马移除Session的数据,不然还没有移除的时候,客户端那边儿的请求又来了,就又能匹配了,产生了重复提交。如果Session域对象数据为空,证明已经提交过数据了!
  • 我们向Session域对象的存入数据究竟是什么呢?简单的一个数字?好像也行啊。因为只要Session域对象的数据和jsp隐藏域带过去的数据对得上号就行了呀,反正在Servlet上判断完是否重复提交,会立马把Session的数据移除掉的。更专业的做法是:向Session域对象存入的数据是一个随机数【Token--令牌】。
  • 生成一个独一无二的随机数
/*
* 产生随机数就应该用一个对象来生成,这样可以避免随机数的重复。
* 所以设计成单例
* */
public class TokenProcessor {
    private TokenProcessor() {
    }
    private final static TokenProcessor TOKEN_PROCESSOR = new TokenProcessor();
    public static TokenProcessor getInstance() {
        return TOKEN_PROCESSOR;
    }
    public static String makeToken() {
        //这个随机生成出来的Token的长度是不确定的
        String token = String.valueOf(System.currentTimeMillis() + new Random().nextInt(99999999));
        try {
            //我们想要随机数的长度一致,就要获取到数据指纹
            MessageDigest messageDigest = MessageDigest.getInstance("md5");
            byte[] md5 = messageDigest.digest(token.getBytes());
            //如果我们直接 return  new String(md5)出去,得到的随机数会乱码。
            //因为随机数是任意的01010101010,在转换成字符串的时候,会查gb2312的码表,gb2312码表不一定支持该二进制数据,得到的就是乱码
            //于是乎经过base64编码成了明文的数据
            BASE64Encoder base64Encoder = new BASE64Encoder();
            return base64Encoder.encode(md5);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}

创建Token随机数,并跳转到jsp页面

 //生出随机数
        TokenProcessor tokenProcessor = TokenProcessor.getInstance();
        String token = tokenProcessor.makeToken();
        //将随机数存进Session中
        request.getSession().setAttribute("token", token);
        //跳转到显示页面
        request.getRequestDispatcher("/login.jsp").forward(request, response);

jsp隐藏域获取到Session的值

<form action="/ouzicheng/Servlet7" >
    用户名:<input type="text" name="username">
    <input type="submit" value="提交" id="button">
    <%--使用EL表达式取出session中的Token--%>
    <input type="hidden" name="token" value="${token}" >
</form>

在处理表单提交页面中判断:jsp隐藏域是否有值带过来,Session中的值是否为空,Session中的值和jsp隐藏域带过来的值是否相等

 String serverValue = (String) request.getSession().getAttribute("token");
        String clientValue = request.getParameter("token");
        if (serverValue != null && clientValue != null && serverValue.equals(clientValue)) {
            System.out.println("处理请求");
            //清除Session域对象数据
            request.getSession().removeAttribute("token");
        }else {
            System.out.println("请不要重复提交数据!");
        }

下面我们再来看一下,已经可以解决表单重复提交的问题了!

0.gif

实现原理是非常简单的:

  • 在session域中存储一个token
  • 然后前台页面的隐藏域获取得到这个token
  • 在第一次访问的时候,我们就判断seesion有没有值,如果有就比对。对比正确后我们就处理请求,接着就把session存储的数据给删除了
  • 等到再次访问的时候,我们session就没有值了,就不受理前台的请求了!

一次性校验码

  • 一次性校验码其实就是为了防止暴力猜测密码
  • 在讲response对象的时候,我们使用response对象输出过验证码,但是没有去验证!
  • 验证的原理也非常简单:生成验证码后,把验证码的数据存进Session域对象中,判断用户输入验证码是否和Session域对象的数据一致
  • 生成验证码图片,并将验证码存进Session域中
(80, 20, BufferedImage.TYPE_INT_RGB);
        //获取到这张图片
        Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
        //设置背景色为白色
        graphics2D.setColor(Color.white);
        graphics2D.fillRect(0, 0, 80, 20);
        //设置图片的字体和颜色
        graphics2D.setFont(new Font(null, Font.BOLD, 20));
        graphics2D.setColor(Color.BLUE);
        //生成随机数
        String randomNum = makeNum();
        //往这张图片上写数据,横坐标是0,纵坐标是20
        graphics2D.drawString(randomNum, 0, 20);
        //将随机数存进Session域中
        request.getSession().setAttribute("randomNum", randomNum);
        //控制浏览器不缓存该图片
        response.setHeader("Expires", "-1");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        //通知浏览器以图片的方式打开
        response.setHeader("Content-type", "image/jpeg");
        //把图片写给浏览器
        ImageIO.write(bufferedImage, "jpg", response.getOutputStream());

生成随机数的方法:

  private String makeNum() {
        Random random = new Random();
        //生成0-6位的随机数
        int num = random.nextInt(999999);
        //验证码的数位全都要6位数,于是将该随机数转换成字符串,不够位数就添加
        String randomNum = String.valueOf(num);
        //使用StringBuffer来拼凑字符串
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < 6 - randomNum.length(); i++) {
            stringBuffer.append("0");
        }
        return stringBuffer.append(randomNum).toString();
    }

jsp显示页面

<form action="/ouzicheng/Login2Servlet">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    验证码:<input type="text" name="randomNum">
    <img src="/ouzicheng/ImageServlet" ><br><br>
    <input type="submit" value="提交">
</form>

处理提交表单数据的Servlet,判断用户带过来验证码的数据是否和Session的数据相同。

  //获取用户输入验证码的数据
        String client_randomNum = request.getParameter("randomNum");
        //获取Session中的数据
        String session_randomNum = (String) request.getSession().getAttribute("randomNum");
        //判断他俩数据是否相等,用户是否有输入验证码,Session中是否为空
        if (client_randomNum == null || session_randomNum == null || !client_randomNum.equals(session_randomNum)) {
            System.out.println("验证码错误了!!!");
            return ;
        }
        //下面就是验证用户名和密码...................

显示页面是这样子的

0.jpg

目录
相关文章
|
3月前
|
JavaScript Java 容器
servlet过滤器Filter简要回顾-过滤请求字符编码,/和/*和/**的区别
本文简要回顾了Servlet过滤器Filter的概念和使用,通过实例演示了如何创建过滤器以过滤请求字符编码,并解释了在web.xml中配置过滤器时使用`/`、`/*`和`/**`的区别。
servlet过滤器Filter简要回顾-过滤请求字符编码,/和/*和/**的区别
|
1月前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
69 4
|
1月前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
2月前
|
缓存 Java Spring
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
文章比较了在Servlet和Spring Boot中获取Cookie、Session和Header的方法,并提供了相应的代码实例,展示了两种方式在实际应用中的异同。
199 3
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
|
2月前
|
存储 安全 数据安全/隐私保护
Cookie 和 Session 的区别及使用 Session 进行身份验证的方法
【10月更文挑战第12天】总之,Cookie 和 Session 各有特点,在不同的场景中发挥着不同的作用。使用 Session 进行身份验证是常见的做法,通过合理的设计和管理,可以确保用户身份的安全和可靠验证。
28 1
|
3月前
|
存储 缓存 数据处理
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
本文介绍了PHP会话控制及Web常用的预定义变量,包括`$_REQUEST`、`$_SERVER`、`$_COOKIE`和`$_SESSION`的用法和示例。涵盖了cookie的创建、使用、删除以及session的工作原理和使用,并通过图书上传的例子演示了session在实际应用中的使用。
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
|
2月前
|
存储 JavaScript 前端开发
vuex和localstorage . cookie的区别
【10月更文挑战第8天】
60 1
|
2月前
|
存储 缓存 JavaScript
cookie和localStorage的区别特点
cookie和localStorage的区别特点
168 0
|
3月前
|
存储 安全 NoSQL
Cookie、Session、Token 解析
Cookie、Session、Token 解析
67 0
|
3月前
|
存储 前端开发 Java
JavaWeb基础7——会话技术Cookie&Session
会话技术、Cookie的发送和获取、存活时间、Session钝化与活化、销毁、用户登录注册“记住我”和“验证码”案例
JavaWeb基础7——会话技术Cookie&Session
下一篇
DataWorks