码迷,mamicode.com
首页 > Web开发 > 详细

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

时间:2015-10-25 22:49:31      阅读:2832      评论:0      收藏:0      [点我收藏+]

标签:java   jsp   验证码   

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

技术分享


一 前台显示页面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;	
	}


本文出自 “zifangsky” 博客,请务必保留此出处http://983836259.blog.51cto.com/7311475/1706029

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

标签:java   jsp   验证码   

原文地址:http://983836259.blog.51cto.com/7311475/1706029

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!