您的位置:首页 > Web前端 > JavaScript

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

2015-10-25 14:50 1041 查看
只需几步就可以生成动态随机的验证码,最终效果如下图:




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

新增一个节点,代码如下:
<!-- 验证码绘制 -->
<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完整代码如下:
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(0, 0, WIDTH, HEIGHT);
}

/**
* 设置图片边框
* */
private void setBordor(Graphics graphics) {
graphics.setColor(Color.BLUE);
graphics.drawRect(1, 1, 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相关代码如下:
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()目的是将一个字符串中的小写字母转换为大写字母,代码如下:
/**
* 将一个字符串中的小写字母转换为大写字母
*
* */
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:当然在这里没有什么必要,可以省略):
/**
* 字符串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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java 验证码 JSP