开发者社区> 技术小阿哥> 正文

在JSP中动态生成随机验证码,登录时后台校验验证码,以及如何避免同一个验证码被重复提交爆破密码

简介:
+关注继续查看

只需几步就可以生成动态随机的验证码,最终效果如下图:

wKioL1YscJvQIeqDAAF7uMWlQBA873.jpg


一 前台显示页面login.jsp

  其中验证码显示的是一张图片,链接指向的是生成验证码的servlet,同时点击图片后触发changeImg()这个js函数,使其动态生成一个新的验证码,这个函数中的参数t=Math.random()并不会参与验证码的生成,它的作用仅仅只是表示每次提交的并不是同一个请求,需要单独处理,完整的login.jsp代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<base href="<%=basePath%>">
<%@ include file="head.txt"%>
<script type="text/javascript">
    //刷新验证码
    function changeImg(){
        document.getElementById("validateCodeImg").src="helpDrawValidateCode?t=" + Math.random();
    }
</script>
</head>
<body>
    <font size=3>
        <center>
            <form action="helpLogin" name="" method="post">
                <table>
                <tr><th>请您登陆:</th></tr>
                    <tr><td align=left>会员名称:</td><td><input type=text name="id"></td></tr>
                    <tr><td align=left>输入密码:</td><td><input type=password name="password"></td></tr>              
                </table
                <br>验证码:<input type="text" name="validateCode" style="width:50px">
                <img alt="看不清?换一张" src="helpDrawValidateCode" id="validateCodeImg" onclick="changeImg()">                        
                <br><input type="submit" value="提交">
            </form>
        </center>
    </font>
</body>
</html>


二 修改web.xml

  新增一个节点,代码如下:

1
2
3
4
5
6
7
8
9
<!-- 验证码绘制 -->
  <servlet>
    <servlet-name>drawValidateCode</servlet-name>
    <servlet-class>com.zifangsky.OnlineFriend.servlet.member.HandleDrawValidateCode</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>drawValidateCode</servlet-name>
    <url-pattern>/helpDrawValidateCode</url-pattern>
  </servlet-mapping>


三 后台的servlet文件HandleDrawValidateCode.java

  这个文件主要负责处理前台请求,并返回生成的验证码图片,同时将图片上的随机字符存入session中,以供登录时进行验证,HandleDrawValidateCode.java完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.zifangsky.OnlineFriend.servlet.member;
 
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
 
import javax.imageio.ImageIO;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
/**
 * 生成随机图片用做验证码
 * */
public class HandleDrawValidateCode extends HttpServlet{
    private static final long serialVersionUID = 1L;
    private static final int WIDTH = 120;  //图片宽度
    private static final int HEIGHT = 30;  //图片高度
         
    public void init(ServletConfig config) throws ServletException{
        super.init(config);
    }
     
    public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
 
        HttpSession session = request.getSession(true);
         
        //创建一张图片
        BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        //得到图片
        Graphics graphics = bufferedImage.getGraphics();
        //设置图片背景色
        setBackGround(graphics);
        //设置图片边框
        setBordor(graphics);
        //在图片上画干扰线,用了4种颜色,共20条线条
        drawRandomLine(graphics,Color.GREEN);
        drawRandomLine(graphics,new Color(246,255,145));
        drawRandomLine(graphics,new Color(225,174,252));
        drawRandomLine(graphics,new Color(120,202,254));
        //在图片上写随机字符,并记录生成的序列
        String randomText = drawRandomText((Graphics2D) graphics);
        //将生成的字符存入session中
        session.setAttribute("checkcode", randomText);
        //设置响应头通知浏览器以图片的形式打开
        response.setContentType("image/jpeg");
        //设置响应头控制浏览器不要缓存
        response.setDateHeader("expries", -1);
        response.setHeader("Cache-Control""no-cache");
        response.setHeader("Pragma""no-cache");
        //将图片写给浏览器
        ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
 
    }
     
    /**
     * 设置图片背景色
     * */
    private void setBackGround(Graphics graphics) {
        graphics.setColor(Color.WHITE);
        graphics.fillRect(00, WIDTH, HEIGHT);
    }
 
    /**
     * 设置图片边框
     * */
    private void setBordor(Graphics graphics) {
        graphics.setColor(Color.BLUE);
        graphics.drawRect(11, WIDTH - 2, HEIGHT - 2);
    }
     
    /**
     * 在图片上画干扰线
     * */
    private void drawRandomLine(Graphics graphics,Color color) {
        graphics.setColor(color);
        //设置线条个数并画线
        for(int i = 0;i < 5;i++){
            int x1 = new Random().nextInt(WIDTH);
            int x2 = new Random().nextInt(WIDTH);
            int y1 = new Random().nextInt(HEIGHT);
            int y2 = new Random().nextInt(HEIGHT);
            graphics.drawLine(x1, y1, x2, y2);
        }
    }
     
    /**
     * 在图片上写随机字符,数字和字母的组合
     * @param length 字符串的长度
     
     * @return 返回生成的字符串序列
     * */
    private String drawRandomText(Graphics2D graphics) {
        graphics.setColor(Color.RED);
        graphics.setFont(new Font("宋体", Font.BOLD, 20));
         
        //数字和字母的组合
        String baseNumLetter = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
        StringBuffer sBuffer = new StringBuffer();
         
        int x = 5;  //旋转原点的 x 坐标
        String ch = "";
        Random random = new Random();
        for(int i = 0;i < 4;i++){
            //设置字体旋转角度
            int degree = random.nextInt() % 30;  //角度小于30度
            int dot = random.nextInt(baseNumLetter.length());
            ch = baseNumLetter.charAt(dot) + ""
            sBuffer.append(ch);
             
            //正向旋转
            graphics.rotate(degree * Math.PI / 180, x, 20);
            graphics.drawString(ch, x, 20);
             
            //反向旋转
            graphics.rotate(-degree * Math.PI / 180, x, 20);
            x += 30;
        }
         
        return sBuffer.toString();
    }
 
     
    public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        doPost(request, response);
    }
}


注:

  在这里,干扰线的颜色和数目都可以自己设定,颜色可以使用随机色,同时显示的每个文字也可以使用随机的颜色,可以增加验证码识别难度,当然在这里我把比较容易混淆的0和O以及I都给去掉了。如果选用经过编码的中文字符的话,也是可以生成中文验证码的


四 login.jsp页面提交表单后,后台的servlet文件HandleLogin.java校验验证码,并进行登录验证

   经过上面的三个步骤后,login.jsp应该是可以正确显示验证码了,同时点击验证码图片后会生成一个新的验证码,login.jsp提交表单后HandleLogin.java文件验证验证码时主要是:将输入的验证码中的小写字母转换成大写,再和验证码生成时保存在session中的字符串比较,如果相同,则表示输入正确,同时移除session中设置的值,防止重复提交,爆破密码,HandleLogin.java相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.zifangsky.OnlineFriend.servlet.member;
 
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
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.zifangsky.OnlineFriend.model.member.Login;
import com.zifangsky.OnlineFriend.util.DbConn;
import com.zifangsky.OnlineFriend.util.StringUtil;
 
public class HandleLogin extends HttpServlet{
    private String backNews = "";  //登录状态返回信息
     
    public void init(ServletConfig config) throws ServletException{
        super.init(config);
    }
     
    public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        HttpSession session = request.getSession(true);
         
        //获取验证码
        String validateCode = StringUtil.xssEncode(request.getParameter("validateCode").trim());
        Object checkcode = session.getAttribute("checkcode");
        //将输入的验证码中的小写字母转换成大写,再和验证码生成时保存在session中的字符串比较     
        if(checkcode != null && checkcode.equals(StringUtil.convertToCapitalString(validateCode))){
            session.removeAttribute("checkcode");
            continueDoPost(request,response);
        }
        else{          
            response.sendRedirect("login.jsp");
            return;
        }
             
    }
     
    private void continueDoPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException{
        //这里的登录后续操作省略
    }
 
    public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        doPost(request, response);
    }
}


注1:上面用到的StringUtil是我写的一个字符串相关的方法类,其中用到的方法convertToCapitalString()目的是将一个字符串中的小写字母转换为大写字母,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
     * 将一个字符串中的小写字母转换为大写字母
     
     * */
    public static String convertToCapitalString(String src)
    {
         char[] array = src.toCharArray();
          int temp = 0;
          for (int i = 0; i < array.length; i++)
          {
              temp = (int) array[i];
              if (temp <= 122 && temp >= 97){ // array[i]为小写字母
                   array[i] = (char) (temp - 32);
              }
           }
           return String.valueOf(array);
       }


注2:其中方法xssEncode()是将输入的字符串进行编码,以此避免产生XSS跨站脚本攻击,用的是commons-lang-2.4.jar里的StringEscapeUtils,代码如下(PS:当然在这里没有什么必要,可以省略):

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
     * 字符串XSS过滤,JavaScript过滤,Sql过滤
     
     * @param str 传入的字符串
     
     * @return 转义后的字符串
     * */
    public static String xssEncode(String str){
        String s = StringEscapeUtils.escapeHtml(str);
//      s = StringEscapeUtils.escapeJavaScript(s);
//      s = StringEscapeUtils.escapeSql(s);      
        return s; 
    }




本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1706029,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
19591 0
SpringBoot-08:SpringBoot采用json的方式实现前后台通用的配置文件
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥-------------     一。需求:   本篇博客是最近笔者做的一个项目,已经上线但是还在不断开发,有些页面上的配置,测试服务器和正式服务器的参数不同,需要经常改动,所以直接改页面肯定不合适!   so;产品经理提出一个需求,需要只建一个配置文件,存储正式服务器和测试服务器的配置,要求前后台通用,读取方便,修改方便 二。
2737 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
14604 0
基于jsp+servlet图书管理系统之后台用户信息删除操作
上一篇的博客写的是修改操作,且附有源码和数据库,这篇博客写的是删除操作,附有从头至尾写的代码(详细的注释)和数据库!   此次删除操作的源码和数据库:http://download.csdn.net/detail/biexiansheng/9732955  自此,基于jsp+servlet开发的用户信息增删该查已经全部写完了,上面的链接是全部的代码,包含增删该查和数据库。   注意点:
1675 0
基于jsp+servlet图书管理系统之后台用户信息修改操作
上一篇的博客写的是查询操作,且附有源码和数据库,这篇博客写的是修改操作,附有从头至尾写的代码(详细的注释)和数据库!   此次修改操作的源码和数据库:http://download.csdn.net/detail/biexiansheng/9732691  为了方便理解和说明,先写一下执行的流程和步奏,详细代码可以下载连接。
905 0
基于jsp+servlet图书管理系统之后台用户信息插入操作
前奏:   刚开始接触博客园写博客,就是写写平时学的基础知识,慢慢发现大神写的博客思路很清晰,知识很丰富,非常又价值,反思自己写的,顿时感觉非常low,有相当长一段时间没有分享自己的知识。于是静下心来钻研知识,趁着这学期的结束(马上就要放寒假了),写写最近练习的基于jsp+servlet+前台模板写的图书管理系统,一点一点写吧,详细的注释已经在代码上说明,希望对学习基于java开发的web方
2766 0
13692
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载