验证码的作用和实现
验证码的作用,很常见的就是登录与注册,或者在贴吧,一些BBS等多处用于防止恶意攻击的。So,web开发者应该学会验证码的设计和实现,我们在开发web项目的时候,当涉及到登录,注册等功能时,我应该采用验证码技术,防止我我们开发的项目是相对于没有验证码的安全点。验证码只是防止机器的批量等暴力攻击,如果是Hacker,那验证码也只是炮灰了 ^—^。
验证码的实现过程:四个步骤搞定~
(由于小弟最近在学习struts,所以本次验证码实现是基于struts的)
一,既然是在struts下开发项目,自然先配置web.xml文件,配置一次就基本没事了。
先在lib下添加以下架包:
commons-fileupload-1.2.1.jar
freemarker-2.3.15.jar
ognl-2.7.3.jar
struts2-core-2.1.8.jar
xwork-core-2.1.6.jar
- web.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.5"
- xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>
- org.apache.struts2.dispatcher.ng.filter.
- StrutsPrepareAndExecuteFilter
- </filter-class>
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </web-app>
以上struts的web.xml配置可以先不管,这个配置是固定的,配置一次就不用再管了。
二,写一个VaildateCodeAction如下:
- package edu.nwsuaf.xc.action;
- import java.awt.image.BufferedImage;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.Map;
- import org.apache.struts2.interceptor.SessionAware;
- import edu.nwsuaf.xc.util.ValidateCodeUtil;
- /**
- * @author tony
- * 2014-12-23
- * 自定义一个Action,并实现SessionAware,需要使用到session保存验证码。
- */
- public class VaildateCodeAction implements SessionAware {
- // 定义一个session为接口注入
- protected Map<String, Object> session;
- // 因为需要将上生成的验证码输出到页面 定义一个输入流
- private InputStream validateCodeStream;
- public String execute() {
- // 生成验证码及图片
Map<String, BufferedImage> valiCodeImag = ValidateCodeUtil.getValidateCode();
- // 验证码
- String valiCode = valiCodeImag.keySet().iterator().next();
- session.put("valiCode", valiCode);
- // 转为输入流
- BufferedImage image = valiCodeImag.get(valiCode);
- try {
- validateCodeStream = ValidateCodeUtil.getAsInputStream(image);
- } catch (IOException e) {
- e.printStackTrace();
- return "error";
- }
- return "success";
- }
- public InputStream getImageStream() {
- return validateCodeStream;
- }
- public void setImageStream(InputStream imageStream) {
- this.validateCodeStream = imageStream;
- }
- /**
- * 实现SessionAware接口为Action注入一个session来 保存验证码
- */
- public void setSession(Map<String, Object> arg0) {
- this.session = arg0;
}
}
三,在struts.xml配置文件中配置VaildateCodeAction如下:
- struts.xml:
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE struts PUBLIC
- "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
- "http://struts.apache.org/dtds/struts-2.1.7.dtd">
- <struts>
- <package name="login" namespace="/login" extends="struts-default">
- <!-- 直接跳转到登录页面 -->
- <action name="toLogin">
- <result name="success">/login.jsp</result>
- </action>
- <!-- 生成验证码Action -->
- <action name="getValidateCode"
- class="edu.nwsuaf.xc.action.VaildateCodeAction">
- <result name="success" type="stream">
- <param name="inputName">imageStream</param>
- </result>
- <result name="error">/error.jsp</result>
- </action>
- </package>
- </struts>
另外还有一个验证码的工具类ValidateCodeUtil ,进行封装便于维护,也体现了Java的编程思想。
- package edu.nwsuaf.xc.util;
- import java.awt.Color;
- import java.awt.Font;
- import java.awt.Graphics;
- import java.awt.image.BufferedImage;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Random;
- import com.sun.image.codec.jpeg.JPEGCodec;
- import com.sun.image.codec.jpeg.JPEGImageEncoder;
- public final class ValidateCodeUtil {
- // 验证码的字符来源
- private static final char[] chars =
- { ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘A‘, ‘B‘, ‘C‘, ‘D‘,‘E‘,
- ‘F‘, ‘G‘, ‘H‘, ‘I‘, ‘J‘, ‘K‘, ‘L‘, ‘M‘,‘N‘, ‘O‘, ‘P‘, ‘Q‘, ‘R‘, ‘S‘, ‘U‘,
- ‘V‘, ‘W‘, ‘X‘, ‘X‘, ‘Y‘, ‘Z‘, ‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘, ‘g‘, ‘h‘, ‘i‘, ‘j‘,
- ‘k‘, ‘l‘, ‘m‘, ‘n‘, ‘o‘, ‘p‘,‘q‘, ‘r‘, ‘s‘, ‘u‘, ‘v‘, ‘w‘, ‘x‘, ‘x‘, ‘y‘, ‘z‘ };
- // 验证码的字符个数
- private static final int VALIDATE_SIZE = 5;
- // 验证码的宽高
- private static final int WIDTH = 130;
- private static final int HEIGHT = 45;
- // 验证码的字体大小
- private static final int FONT_SIZE = 35;
- private static Random ran = new Random();
- public static Map<String, BufferedImage> getValidateCode() {
- // 存放验证码字符串
- StringBuffer validate_code = new StringBuffer();
- BufferedImage blackground = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
- Graphics graphic = blackground.getGraphics();
- graphic.setColor(Color.LIGHT_GRAY);
- graphic.fillRect(0, 0, WIDTH, HEIGHT);
- // 画随机字符
- for (int i = 1; i <= VALIDATE_SIZE; i++) {
- int r = ran.nextInt(chars.length);
- graphic.setColor(getRandomColor());
- // 设置画笔的字体和大小
- graphic.setFont(new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE));
- graphic.drawString(chars[r] + "", (i - 1) * WIDTH / VALIDATE_SIZE, 35);
- // 将字符保存
- validate_code.append(chars[r]);
- }
- // 画干 5 条扰线
- for (int i = 1; i <= 5; i++) {
- graphic.setColor(getRandomColor());
- graphic.drawLine(ran.nextInt(WIDTH), ran.nextInt(HEIGHT),
- ran.nextInt(WIDTH), ran.nextInt(HEIGHT));
- }
- Map<String, BufferedImage> map = new HashMap<String, BufferedImage>();
- map.put(validate_code.toString(), blackground);
- return map;
- }
- /**
- * 随机获取颜色
- * @return
- */
- private static Color getRandomColor() {
- return new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));
- }
- /**
- * 将图片转化为流
- * @param image
- * @return
- * @throws IOException
- */
public static InputStream getAsInputStream(BufferedImage image) throws IOException {
- // ByteArrayOutputStream 自动增长的byte缓冲区
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(baos);
- encoder.encode(image);
- byte[] imageBts = baos.toByteArray();
- InputStream inStream = new ByteArrayInputStream(imageBts);
- return inStream;
- }
- }
其实对于验证码这种比较“孤立”的组件,我们在开发时,写一次的源码的实现,即可保存下来,下次当我们有遇到需要实现验证码功能时,直接copy来用,岂不是大大的提高开发效率。
四,login.jsp页面如下:(实现点击验证码更换验证码)
- login.jsp:
- <%@ page language="java" pageEncoding="UTF-8"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
- <meta http-equiv="description" content="This is my page">
- <script type="text/javascript" language="javascript">
- function change(imageObj) {
- imageObj.src = "login/getValidateCode?date="
- + new Date().getTime();
- }
- </script>
- </head>
- <body>
- <div>
- 验证码:<input name="" type="text"/>
- <img alt="验证码" title="点击更换"
- src="login/getValidateCode.action" onclick="change(this);">
- </div>
- </body>
- </htm
目录结构如图:
测试: 输入http://ip:port/webNamePath
打开浏览器输入 http://localhost:8089/ValidateCode/login/toLogin.action
................
- Struts的前端控制器FC的配置;
web.xml的简单配置;
struts.xml的简单配置;
验证码的实现工具类Util 。