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

目录
相关文章
|
1月前
|
存储 JavaScript 前端开发
学习vuex和localstorage . cookie的作用与区别
探讨Vuex、LocalStorage与Cookie:三种关键技术在现代Web开发中的角色。Vuex作为Vue的状态管理工具,提供集中、响应式且可预测的状态变更机制,适用于复杂应用。LocalStorage为客户端提供大容量、持久化的数据存储方案,适合保存用户偏好等静态信息。Cookie则擅长会话跟踪与认证管理,数据虽小却能在客户端与服务器间传递。每种技术针对不同场景各有优势,合理选用是关键。
|
6天前
|
API
|
9天前
|
存储 消息中间件 前端开发
Web2py框架下的神秘力量:如何轻松集成第三方API,让你的应用不再孤单!
【8月更文挑战第31天】在开发现代Web应用时,常需集成第三方服务如支付网关、数据存储等。本文将指导你使用Web2py框架无缝接入第三方API。通过实例演示从注册获取API密钥、创建控制器、发送HTTP请求到处理响应的全过程。利用`requests`库与Web2py的内置功能,轻松实现API交互。文章详细介绍了如何编写RESTful控制器,处理API请求及响应,确保数据安全传输。通过本教程,你将学会如何高效整合第三方服务,拓展应用功能。欢迎留言交流心得与建议。
24 1
|
16天前
|
前端开发 API 网络架构
【Azure 应用服务】能否通过 Authentication 模块配置 Azure AD 保护 API 应用?
【Azure 应用服务】能否通过 Authentication 模块配置 Azure AD 保护 API 应用?
|
1月前
|
人工智能 Serverless API
AI 创业及变现新思路:零门槛 AI 绘图,定制 ComfyUI Serverless API 应用
为了帮助用户高效率、低成本应对企业级复杂场景,本文介绍 ComfyUI API Serverless 版解决方案,通过使用该方案,用户可以充分利用 ComfyUI +Serverless 技术优势快速开发上线 AI 绘画应用,期待为广大开发者 AI 绘画创业及变现提供思路。
|
29天前
|
移动开发 API 开发者
什么是HTML5 History API有哪些应用场景
【8月更文挑战第11天】什么是HTML5 History API有哪些应用场景
32 1
|
10天前
|
API 开发者 Python
API接口:原理、实现及应用
本文详细介绍了API接口在现代软件开发中的重要性及其工作原理。API接口作为应用程序间通信的桥梁,通过预定义的方法和协议实现数据和服务的共享。文章首先解释了API接口的概念,接着通过Python Flask框架示例展示了API的设计与实现过程,并强调了安全性的重要性。最后,本文还讨论了API接口在Web服务和移动应用程序等领域的广泛应用场景。
|
13天前
|
数据采集 物联网 数据挖掘
API接口的应用
API接口在现代技术中至关重要,它使不同软件、设备间能相互通信和数据共享。在社交网络中,如Facebook及Twitter的API让开发者能够构建交互式应用;移动应用则依赖API与服务器通信,实现天气查询、地图定位等功能;云计算平台如AWS通过API提供了资源管理和配置服务;物联网设备使用API实现数据交换;视频游戏开发商利用各类平台API发布游戏。此外,API爬虫数据接口技术通过爬虫抽取并输出数据,被广泛应用于获取商业、金融、医疗等领域的大数据,以支持数据分析、策略制定及业务流程优化,极大地提高了数据收集与处理的效率和准确性。
|
15天前
|
JavaScript 前端开发 Linux
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()
|
16天前
|
API
【Azure 环境】在Azure活动目录中的应用注册,给应用添加API权限时发现API权限配置缺失
【Azure 环境】在Azure活动目录中的应用注册,给应用添加API权限时发现API权限配置缺失