JavaEE--验证码(十二)

一、验证码

验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。

二、Servlet图片验证码实现

图片验证的实现,我们可以直接将生成图片的代码和文字资源结合在一起,通过后台去读取文字资源中的文字,将资源文件放入/WEB-INF/new_words.txt

平易近人
宽宏大度
冰清玉洁
持之以恒
锲而不舍
废寝忘食
大义凛然
临危不俱
光明磊落
不屈不挠
鞠躬尽瘁
死而后已

代码:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/**
 * 验证码生成程序
 */
public class CheckWord extends HttpServlet {
 
    // 集合中保存所有成语
    private List<String> words = new ArrayList<String>();
 
    @Override
    public void init() throws ServletException {
        // 初始化阶段,读取new_words.txt
        // web工程中读取 文件,必须使用绝对磁盘路径
        String path = getServletContext().getRealPath("/WEB-INF/new_words.txt");
        try {
            BufferedReader reader = new BufferedReader(new FileReader(path));
            String line;
            while ((line = reader.readLine()) != null) {
                words.add(line);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 禁止缓存
        // response.setHeader("Cache-Control", "no-cache");
        // response.setHeader("Pragma", "no-cache");
        // response.setDateHeader("Expires", -1);
 
        int width = 120;
        int height = 30;
 
        // 步骤一 绘制一张内存中图片
        BufferedImage bufferedImage = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
 
        // 步骤二 图片绘制背景颜色 ---通过绘图对象
        Graphics graphics = bufferedImage.getGraphics();// 得到画图对象 --- 画笔
        // 绘制任何图形之前 都必须指定一个颜色
        graphics.setColor(getRandColor(200, 250));
        graphics.fillRect(0, 0, width, height);
 
        // 步骤三 绘制边框
        graphics.setColor(Color.WHITE);
        graphics.drawRect(0, 0, width - 1, height - 1);
 
        // 步骤四 四个随机数字
        Graphics2D graphics2d = (Graphics2D) graphics;
        // 设置输出字体
        graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
 
        Random random = new Random();// 生成随机数
        int index = random.nextInt(words.size());
        String word = words.get(index);// 获得成语
 
        // 定义x坐标
        int x = 10;
        for (int i = 0; i < word.length(); i++) {
            // 随机颜色
            graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random
                    .nextInt(110), 20 + random.nextInt(110)));
            // 旋转 -30 --- 30度
            int jiaodu = random.nextInt(60) - 30;
            // 换算弧度
            double theta = jiaodu * Math.PI / 180;
 
            // 获得字母数字
            char c = word.charAt(i);
 
            // 将c 输出到图片
            graphics2d.rotate(theta, x, 20);
            graphics2d.drawString(String.valueOf(c), x, 20);
            graphics2d.rotate(-theta, x, 20);
            x += 30;
        }
 
        // 将验证码内容保存session
        request.getSession().setAttribute("checkcode_session", word);
 
        // 步骤五 绘制干扰线
        graphics.setColor(getRandColor(160, 200));
        int x1;
        int x2;
        int y1;
        int y2;
        for (int i = 0; i < 30; i++) {
            x1 = random.nextInt(width);
            x2 = random.nextInt(12);
            y1 = random.nextInt(height);
            y2 = random.nextInt(12);
            graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
        }
 
        // 将上面图片输出到浏览器 ImageIO
        graphics.dispose();// 释放资源
        
        //将图片写到response.getOutputStream()中
        ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
 
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
 
    /**
     * 取其某一范围的color
     * 
     * @param fc
     *            int 范围参数1
     * @param bc
     *            int 范围参数2
     * @return Color
     */
    private 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);
    }
 
}

JSP代码:

<%@ page language="java" import="java.util.*" 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">
<html>
<head>
<base href="<%=basePath%>">
    
<title>登录页面</title>
    <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">
        function checkImg(object){
            object.src="/mark/checkWord?time=" + new Date().getTime();
        }
    </script>
</head>
  
<body>
    <form action="/C3P0Test/UserSQL" method="post">
        <div>
            用户名:<input type="text" name="username">
        </div>
        <div>
            密码:<input type="password" name="password">
        </div>
        <div>
            验证码:<input type="text" name="checkWord">
            <span>
                <img alt="" src="/mark/checkWord" onclick="checkImg(this)">
            </span>
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>
</body>
</html>

三、Kaptcha

Kaptcha是一个非常实用的验证码生成工具,可以通过配置生成多样化的验证码。
Kaptcha 是一个可高度配置的实用验证码生成工具,可自由配置的选项如:

验证码的字体
验证码字体的大小
验证码字体的字体颜色
验证码内容的范围(数字,字母,中文汉字!)
验证码图片的大小,边框,边框粗细,边框颜色
验证码的干扰线
验证码的样式(鱼眼样式、3D、普通模糊、...)
Kaptcha 是一个可高度配置的实用验证码生成工具,可自由配置的选项如:

Kaptcha 详细配置表

Constant 描述 默认值
kaptcha.border 图片边框,合法值:yes , no yes
kaptcha.border.color 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black
kaptcha.image.width 图片宽 200
kaptcha.image.height 图片高 50
kaptcha.producer.impl 图片实现类 com.google.code.kaptcha.impl.DefaultKaptcha
kaptcha.textproducer.impl 文本实现类 com.google.code.kaptcha.text.impl.DefaultTextCreator
kaptcha.textproducer.char.string 文本集合,验证码值从此集合中获取 abcde2345678gfynmnpwx
kaptcha.textproducer.char.length 验证码长度 5
kaptcha.textproducer.font.names 字体 Arial, Courier
kaptcha.textproducer.font.size 字体大小 40px.
kaptcha.textproducer.font.color 字体颜色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.textproducer.char.space 文字间隔 2
kaptcha.noise.impl 干扰实现类 com.google.code.kaptcha.impl.DefaultNoise
kaptcha.noise.color 干扰 颜色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.obscurificator.impl 图片样式:
水纹 com.google.code.kaptcha.impl.WaterRipple
鱼眼 com.google.code.kaptcha.impl.FishEyeGimpy
阴影 com.google.code.kaptcha.impl.ShadowGimpy
com.google.code.kaptcha.impl.WaterRipple
kaptcha.background.impl 背景实现类 com.google.code.kaptcha.impl.DefaultBackground
kaptcha.background.clear.from 背景颜色渐变,开始颜色 light grey
kaptcha.background.clear.to 背景颜色渐变, 结束颜色 white
kaptcha.word.impl 文字渲染器 com.google.code.kaptcha.text.impl.DefaultWordRenderer
kaptcha.session.key session key KAPTCHA_SESSION_KEY
kaptcha.session.date session date KAPTCHA_SESSION_DATE

1、Maven依赖

<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

2、Servlet使用

1)Servlet
@WebServlet(name = "kaptchaServlet",
        urlPatterns = "/kaptchaServlet",
        initParams = {
                //是否有边框
                @WebInitParam(name= "kaptcha.border", value="no"),
                //字体颜色
                @WebInitParam(name= "kaptcha.textproducer.font.color", value= "red"),
                //图片宽度
                @WebInitParam(name= "kaptcha.image.width", value= "135"),
                //使用哪些字符生成验证码
                @WebInitParam(name= "kaptcha.textproducer.char.string", value= "ACDEFHKPRSTWX345678"),
                //图片高度
                @WebInitParam(name= "kaptcha.image.height", value= "50"),
                //字体大小
                @WebInitParam(name= "kaptcha.textproducer.font.size", value= "43"),
                //干扰线的颜色
                @WebInitParam(name= "kaptcha.noise.color", value= "black"),
                //字符的个数
                @WebInitParam(name= "kaptcha.textproducer.char.length", value= "4"),
                //字体
                @WebInitParam(name= "kaptcha.textproducer.font.names", value= "Arial")
        })
public class kaptchaServlet extends com.google.code.kaptcha.servlet.KaptchaServlet {
}

2) HTML
 <img id="kaptcha-img" alt="点击更换" title="点击更换"
         onclick="changeVerifyCode(this)" src="/kaptchaServlet" />
<script language="JavaScript">
    function changeVerifyCode(img) {
        img.src="/kaptchaServlet?" + Math.floor(Math.random()*100);
    }
</script>

3、在webapp初始化的时候,将Kaptcha加到servletContext中

SpringMVC:

public class WebAppInit implements WebApplicationInitializer {
    @Override
    public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(WebMvcConfig.class);
        // ac.refresh();此处应该注释,否则报错

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletContext.addServlet("DispatcherServlet", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/");

        //配置生成验证码的kaptchaServlet
        //第一步,创建一个kaptchaServlet
        KaptchaServlet kaptchaServlet = new KaptchaServlet();
        //添加到servletContext
        ServletRegistration.Dynamic kaptchaServlet1 = servletContext.addServlet("KaptchaServlet", kaptchaServlet);
        //创建一个map用来存放kapchaServlet的初始化参数
        HashMap<String, String> map = new HashMap<>();
        // <!-- 是否有边框 --> yes or no
        map.put("kaptcha.border", "no");
        //<!-- 字体颜色 -->
        map.put("kaptcha.textproducer.font.color", "red");
        // <!-- 图片宽度 -->
        map.put("kaptcha.image.width", "135");
        //  <!-- 使用哪些字符生成验证码 -->
        map.put("kaptcha.textproducer.char.string", "ACDEFHKPRSTWX345679");
        //  <!-- 图片高度 -->
        map.put("kaptcha.image.height", "50");
        // <!-- 字体大小 -->
        map.put("kaptcha.textproducer.font.siz", "43");
        //  <!-- 干扰线颜色 -->
        map.put("kaptcha.noise.color", "black");
        // <!-- 验证码字符个数 -->
        map.put("kaptcha.textproducer.char.length", "4");
        //<!-- 字体 -->
        map.put("kaptcha.textproducer.font.names", "Arial");
        //设置kapchaServlet的初始化参数
        kaptchaServlet1.setInitParameters(map);
        //设置
        kaptchaServlet1.addMapping("/Kaptcha");
    }
}

3.在前端页面用src的属性引入

<img id="captcha_img" alt="点击更换" title="点击更换"
     src="/Kaptcha" />

可以用js给这个图片添加一个点击事件,点击之后就更换一个图片

com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY
//获取验证码值
(String)servletRequest.getSession().getAttribute("KAPTCHA_SESSION_KEY");

4、SpringBoot使用

1)配置文件
@Component
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha getDefaultKaptcha() {
        com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
        Properties properties = new Properties();
        // 图片边框
        properties.setProperty("kaptcha.border", "yes");
        // 边框颜色
        properties.setProperty("kaptcha.border.color", "105,179,90");
        // 字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "red");
        // 图片宽
        properties.setProperty("kaptcha.image.width", "110");
        // 图片高
        properties.setProperty("kaptcha.image.height", "40");
        // 字体大小
        properties.setProperty("kaptcha.textproducer.font.size", "30");
        // session key
        properties.setProperty("kaptcha.session.key", "code");
        // 验证码长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        // 字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);

        return defaultKaptcha;
    }
}

2)控制器

@Controller
@RequestMapping(value = "/front/member")
public class KaptchaController {

    /**
     * 1、验证码工具
     */
    @Autowired
    DefaultKaptcha defaultKaptcha;

    /**
     * 2、生成验证码
     * @param httpServletRequest
     * @param httpServletResponse
     * @throws Exception
     */
    @RequestMapping("/defaultKaptcha")
    public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws Exception {
        byte[] captchaChallengeAsJpeg = null;
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        try {
            // 生产验证码字符串并保存到session中
            String createText = defaultKaptcha.createText();
            httpServletRequest.getSession().setAttribute("verificationCode", createText);
            // 使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
            BufferedImage challenge = defaultKaptcha.createImage(createText);
            ImageIO.write(challenge, "jpg", jpegOutputStream);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        // 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaChallengeAsJpeg);
        responseOutputStream.flush();
        responseOutputStream.close();
    }

    /**
     * 3、校对验证码
     * @param httpServletRequest
     * @return
     */
    @RequestMapping("/imgvrifyControllerDefaultKaptcha")
    public ModelAndView imgvrifyControllerDefaultKaptcha(HttpServletRequest httpServletRequest) {
        ModelAndView andView = new ModelAndView();
        String rightCode = (String) httpServletRequest.getSession().getAttribute("verificationCode");
        String tryCode = httpServletRequest.getParameter("tryCode");
        System.out.println("rightCode:"+rightCode+" ———— tryCode:"+tryCode);
        if (!rightCode.equals(tryCode)) {
            andView.addObject("info", "错误的验证码");
            andView.setViewName("index");
        } else {
            andView.addObject("info", "登录成功");
            andView.setViewName("success");
        }
        return andView;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容

  • 验证码定义:验证码是一种区分用户是计算机还是人的公共自动程序。作用:可以防止恶意破解密码,刷票、论坛灌水,有效防止...
    年少懵懂丶流年梦阅读 769评论 0 1
  • 前言: 关于kaptcha简介以及spring整合kaptcha以及在Linux上验证码显示乱码问题,我在另一篇文...
    贪挽懒月阅读 30,199评论 16 91
  • 源码项目地址 验证码实现 关于kaptcha kaptcha 是一个很有用的验证码生成工具。有了它,你能够生成各种...
    GCYML阅读 1,755评论 1 10
  • 2018年8月4日 今天早起拜完6个忏后,大家吃了些东西后,在七点多就在老师的带领下去放生了。 大家穿着统一的衣服...
    甘愿欢喜HjP阅读 170评论 0 0
  • 念一 廿一
    元尔阅读 143评论 0 0