【java图片验证码】Java 服务端生成图片验证码及验证

目录

简介

分析

代码实现:

测试与运行


简介

验证码是防止有人利用机器人自动批量注册、对特定的注册用户用特定程序暴力破解方 式进行不断的登录、灌水。因为验证码是一个混合了数字或符号的图片,人眼看起来都费劲, 机器识别起来就更困难。

分析

登录表单很可能遭到模拟登录的暴力破解攻击,要么轻易获得特定账户的登录信息,要么给服务器增加了大量的负荷。解决的办法,一般就是在登录前给出一个随机的信息(验证码),非法的非 Web 途径登录者会看不到这个验证码,从而让用户安全登录。为防止攻击者破获验证码,需将验证信息作为图像显示在 Web 上。

代码实现:

利用 Servlet 实现一个 4 位的彩色验证码数据。

 import java.io.*;
 import java.awt.*;
 import java.awt.image.*;
 import java.util.*;
 import javax.imageio.*;
 import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 ​
 @WebServlet("/Image")
 public class Image extends HttpServlet {
 ​
     protected void processRequest(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         response.setContentType("image/jpeg");
         //禁止图像缓存
         response.setHeader("Pragma","No-cache");
         response.setHeader("Cache-Control","no-cache");
         response.setDateHeader("Expires", 0);
         HttpSession session=request.getSession();
         // 在内存中创建图象
         int width=100, height=40;
         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
         // 获取图形上下文
         Graphics g = image.getGraphics();
         //生成随机类
         Random random = new Random();
         // 设定背景色
         g.setColor(getRandColor(200,250));
         g.fillRect(0, 0, width, height);
         //设定字体
         g.setFont(new Font("Times New Roman",Font.PLAIN,28));        
         // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
         g.setColor(getRandColor(160,200));
         for (int i=0;i<155;i++) {
             int x = random.nextInt(width);
             int y = random.nextInt(height);
             int xl = random.nextInt(12);
             int yl = random.nextInt(12);
             g.drawLine(x,y,x+xl,y+yl);
         }
         // 取随机产生的认证码(4位数字)
         String sRand="";
         //准备一个数字加字母字典
         String string = "1234567890abcdefghijklmnopqrstuvwxyz";
         for (int i=0;i<4;i++){
             char rand=string.charAt(random.nextInt(string.length()));
             sRand+=rand;
             // 将认证码显示到图象中
             //调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
             g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
             g.drawString(Character.toString(rand),13*i+25,30);
         }
         // 将认证码存入SESSION
         session.setAttribute("rand",sRand);
         // 图象生效
         g.dispose();
         ServletOutputStream responseOutputStream =response.getOutputStream();
         // 输出图象到页面
         ImageIO.write(image, "JPEG", responseOutputStream);
         //以下关闭输入流!
         responseOutputStream.flush();
         responseOutputStream.close();
     }
 ​
     //给定范围获得随机颜色
     Color getRandColor(int fc,int bc){
         Random random = new Random();
         if(fc>255) fc=255;
         if(bc>255) bc=255;
         int r=fc+random.nextInt(bc-fc);
         int g=fc+random.nextInt(bc-fc);
         int b=fc+random.nextInt(bc-fc);
         return new Color(r,g,b);
     }
 ​
     protected void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         processRequest(request, response);
     }
 ​
     protected void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         processRequest(request, response);
     }
 }

写完后保存文件,启动服务器,访问 Servlet 后可以看到页面中生成了一张图片验证码

测试与运行

该 Servlet 仅仅是一个验证码图像,接下来我们用 jsp 编写一个包含该验证码的登录页面

jsp 程序代码如下:

 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <%
     String path = request.getContextPath();
     String basePath = request.getScheme() + "://" + request.getServerName() + ":" +request.getServerPort() + path;
 %>
 ​
 <!DOCTYPE HTML>
 <html>
 <head>
     <title>表单验证</title>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
     <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
     <META HTTP-EQUIV="Expires" CONTENT="0">
     <style>
         div {
             width: 240px;
             height: 100%;
             margin: 30px auto;
         }
 ​
         label {
             margin: 5px auto;
             height: 22px;
             line-height: 22px;
             float: left;
         }
 ​
         .input {
             margin: 5px auto;
             height: 20px;
             float: right;
         }
 ​
         #submit {
             width: 100px;
             height: 35px;
             margin: 5px 8px;
         }
     </style>
 </head>
 ​
 <body>
 <div>
     <form id="myForm" action="" method="post" autocomplete="off" >
         <label>用户名:</label><input class="input" type="text" name="name" placeholder="请输入用户名"><br>
         <label>密码:</label><input class="input" type="password" name="password" placeholder="请输入密码"><br>
         <div style="margin-top: 10px;">
             <div style="height: 10px;width: 200px;margin-bottom: 0px"> </div>
             <input style="width: 110px;height: 33px;margin-right: 20px;float: left " type="text" placeholder="图片验证码" name="code">
             <img style="float: right" title="看不清?点击切换。" id="changeServletImg" src="<%=basePath%>/Image/Image">
         </div>
         <input style="margin-left: 140px" id="submit" type="button" value="登录">
     </form>
     <div id="errorTips" style="text-align: center;font-size: 18px;color: red"></div>
 </div>
 </body>
 <script>
     window.onload = function () {
 ​
         var changeServletImgDOM = document.getElementById('changeServletImg');
         //点击图片时改变验证码
         changeServletImgDOM.onclick=change;
 ​
         function change() {
             changeServletImgDOM.src="<%=basePath%>/Image/Image?"+new Date().getTime();
         };
 ​
         document.getElementById('submit').onclick = check;
 ​
         function check () {
 ​
             //获取表单数据
             var name = document.getElementsByName('name')[0].value;
             var password = document.getElementsByName('password')[0].value;
             var code = document.getElementsByName('code')[0].value;
             var formInfo = 'name=' + name + '&password=' + password + '&code=' + code;
             var xhr = new XMLHttpRequest();
             //设置请求参数
             xhr.open("post","Example_13",true);
             xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8");
             //发送请求
             xhr.send(formInfo);
             // 设置响应 HTTP 请求状态变化的函数
             xhr.onreadystatechange = function() {
                 if (xhr.status == 200 && xhr.readyState == 4) {
                     if(xhr.responseText == 'OK') {
                         // 验证成功时跳转页面
                         location.href = 'loginSuccess.html';
                     } else {
                         // 验证失败时给出错误提示
                         document.getElementById('errorTips').innerText = xhr.responseText;
                     }
                 }
             }
         }
     }
 </script>
 </html>

loginSuccess.html 代码如下:

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>欢迎</title>
 </head>
 <body>
 <h1>登录成功!</h1>
 </body>
 </html>

由于 jsp 先于 java 执行,导致 jsp 页面图片验证码获取的session值始终是示前一个。

所以无法直接在填写表单的页面完成验证码的验证。

接下来编写一个 Servlet 来接收由 AJAX 发送的表单数据,然后通过 AJAX 完成错误信息的回显,以及页面跳转。

(为了方便,这里用 equals 进行验证,不连接数据库) 【用户名和密码为 admin】

servlet 程序代码如下:

import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.IOException;
 import java.io.PrintWriter;
 ​
 @WebServlet("/Example_13")
 public class Example_13 extends HttpServlet {
     
     //判断用户是否存在 (为了方便,这里用 equals 进行验证,不连接数据库)
     public boolean isAdmin(String name,String password){
         //假设用户不存在
         boolean isExist = false;
         if("admin".equals(name) && "admin".equals(password)) {
             isExist = true;
         }
         return isExist;
     }
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
         doPost(req, resp);
     }
 ​
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
         req.setCharacterEncoding("utf-8");
         resp.setContentType("text/html;charset=utf-8");
         PrintWriter out = resp.getWriter();
         //获取表单数据
         String name = req.getParameter("name");
         String password = req.getParameter("password");
         String code = req.getParameter("code");
 ​
         //获取图片验证码
         HttpSession session =  req.getSession();
         String rand = (String)session.getAttribute("rand");
         //验证图片验证码
         if(rand.equals(code)) {
             getConnect();
             //验证用户名 密码
             if(isAdmin(name,password)) {
                 closeConnect();
                 out.print("OK");
             } else {
                 out.print("用户名或密码错误!");
             }
         } else {
             out.print("验证码错误,验证失败!");
         }
     }
 }

效果如下:

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
验证客户端和服务端的时间是否一致,可以使用以下步骤: 1. 服务端获取当前时间戳,可以使用System.currentTimeMillis()方法获取。 2. 服务端将当前时间戳发送给客户端。 3. 客户端获取当前时间戳,并将其与服务端发送的时间戳进行比较。 4. 如果客户端和服务端的时间戳差距在可接受的范围内,则表明客户端和服务端的时间是一致的;否则表明客户端和服务端的时间不一致。 具体实现方式可以使用Java的Socket编程来实现。服务端可以使用ServerSocket类来监听客户端请求,客户端可以使用Socket类来与服务端建立连接。服务端和客户端之间可以通过IO流来进行数据的传输。 实现代码如下: 服务端: ``` ServerSocket serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); long timestamp = System.currentTimeMillis(); DataOutputStream out = new DataOutputStream(socket.getOutputStream()); out.writeLong(timestamp); out.flush(); socket.close(); ``` 客户端: ``` Socket socket = new Socket("localhost", 8888); DataInputStream in = new DataInputStream(socket.getInputStream()); long serverTimestamp = in.readLong(); long clientTimestamp = System.currentTimeMillis(); if (Math.abs(clientTimestamp - serverTimestamp) <= ACCEPTABLE_DIFFERENCE) { System.out.println("客户端时间和服务端时间一致"); } else { System.out.println("客户端时间和服务端时间不一致"); } socket.close(); ``` 其中ACCEPTABLE_DIFFERENCE为可接受的时间戳差距。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hi 小朋友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值