精华内容
下载资源
问答
  • 验证码

    2016-05-22 10:34:00
    验证码

    常见验证码的弱点与验证码识别

    0x00 简介


    验证码作为一种辅助安全手段在Web安全中有着特殊的地位,验证码安全和web应用中的众多漏洞相比似乎微不足道,但是千里之堤毁于蚁穴,有些时候如果能绕过验证码,则可以把手动变为自动,对于Web安全检测有很大的帮助。

    全自动区分计算机和人类的图灵测试(英语:Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA),俗称验证码,是一种区分用户是计算机和人的公共全自动程序。在CAPTCHA测试中,作为服务器的计算机会自动生成一个问题由用户来解答。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。(from wikipedia)

    大部分验证码的设计者都不知道为什么要用到验证码,或者对于如何检验验证码的强度没有任何概念。大多数验证码在实现的时候只是把文字印到背景稍微复杂点的图片上就完事了,程序员没有从根本上了解验证码的设计理念。

    验证码的形式多种多样,先介绍最简单的纯文本验证码。

    纯文本验证码

    纯文本,输出具有固定格式,数量有限,例如:

    •1+1=?
    
    •本论坛的域名是?
    
    •今天是星期几?
    
    •复杂点的数学运算
    

    这种验证码并不符合验证码的定义,因为只有自动生成的问题才能用做验证码,这种文字验证码都是从题库里选择出来的,数量有限。破解方式也很简单,多刷新几次,建立题库和对应的答案,用正则从网页里抓取问题,寻找匹配的答案后破解。也有些用随机生成的数学公式,比如 随机数 [+-*/]随机运算符 随机数=?,小学生水平的程序员也可以搞定……

    这种验证码也不是一无是处,对于很多见到表单就来一发的spam bot来说,实在没必要单独为了一个网站下那么大功夫。对于铁了心要在你的网站大量灌水的人,这种验证码和没有一样。

    下面讲的是验证码中的重点,图形验证码。

    图形验证码

    先来说一下基础:

    识别图形验证码可以说是计算机科学里的一项重要课题,涉及到计算机图形学,机器学习,机器视觉,人工智能等等高深领域……

    简单地说,计算机图形学的主要研究内容就是研究如何在计算机中表示图形、以及利用计算机进行图形的计算、处理和显示的相关原理与算法。图形通常由点、线、面、体等几何元素和灰度、色彩、线型、线宽等非几何属性组成。计算机涉及到的几何图形处理一般有 2维到n维图形处理,边界区分,面积计算,体积计算,扭曲变形校正。对于颜色则有色彩空间的计算与转换,图形上色,阴影,色差处理等等。

    在破解验证码中需要用到的知识一般是 像素,线,面等基本2维图形元素的处理和色差分析。常见工具为:

    •支持向量机(SVM)
    
    •OpenCV
    
    •图像处理软件(Photoshop,Gimp…)
    
    •Python Image Library
    

    支持向量机SVM是一个机器学习领域里常用到的分类器,可以对图形进行边界区分,不过需要的背景知识太高深。

    OpenCV是一个很常用的计算机图像处理和机器视觉库,一般用于人脸识别,跟踪移动物体等等,对这方面有兴趣的可以研究一下

    PS,GIMP就不说了,说多了都是泪啊……

    Python Image Library是pyhon里面带的一个图形处理库,功能比较强大,是我们的首选。

    20130605190615_98443.png

    SVM图像边界区分

    20130605192502_65273.png

    SVM原理,把数据映射到高维空间,然后寻找能够分割的超平面

    识别验证码需要充分利用图片中的信息,才能把验证码的文字和背景部分分离,一张典型的jpeg图片,每个像素都可以放在一个5维的空间里,这5个维度分别是,X,Y,R,G,B,也就是像素的坐标和颜色,在计算机图形学中,有很多种色彩空间,最常用的比如RGB,印刷用的CYMK,还有比较少见的HSL或者HSV,每种色彩空间的维度都不一样,但是可以通过公式互相转换。

    20130605193040_40334.png

    RGB色彩空间构成的立方体,每个维度代表一种颜色

    20130605193155_34999.png

    HSL(色相饱和度)色彩空间构成的锥体,可以参考:

    https://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4

    了解到色彩空间的原理,就可以用在该空间适用的公式来进行像素的色差判断,比如RGB空间里判断两个点的色差可以用3维空间中两坐标求距离的公式:

    distance=sqrt[(r1-r2)^2+(g1-g2)^2+(b1-b2)^2]

    更加直观的图片,大家感受一下:

    20130605194036_39590.png

    随便把一张图片的每个像素都映射到RGB色彩空间里就能获得一个这样的立方体。

    通过对像素颜色进行统计和区分,可以获得图片的颜色分布,在验证码中,一般来说使用近似颜色最多的像素都是背景,最少的一般为干扰点,干扰线和需要识别文字本身。

    对于在RGB空间中不好区分颜色,可以把色彩空间转换为HSV或HSL:

    20130605194730_56543.png

    0x01 验证码识别的原理和过程


    第一步:    二值化

    所谓二值化就是把不需要的信息通通去除,比如背景,干扰线,干扰像素等等,只剩下需要识别的文字,让图片变成2进制点阵。

    20130605195023_13222.png

    第二步: 文字分割

    为了能识别出字符,需要对要识别的文字图图片进行分割,把每个字符作为单独的一个图片看待。

    20130605200952_55267.png

    第三步:标准化

    对于部分特殊的验证码,需要对分割后的图片进行标准化处理,也就是说尽量把每个相同的字符都变成一样的格式,减少随机的程度

    最简单的比如旋转还原,复杂点的比如扭曲还原等等

    第四步:识别

    这一步可以用很多种方法,最简单的就是模板对比,对每个出现过的字符进行处理后把点阵变成字符串,标明是什么字符后,通过字符串对比来判断相似度。

    在文章的后半部分会详细解释每步的各种算法

    20130605220655_80781.png

    二值化算法

    对于大部分彩色验证码,通过判断色差和像素分布都能准确的把文字和背景分离出来,通过PS等工具把图片打开,用RGB探针对文字和背景图的颜色分别测试,在测试多张图片后,很容易可以发现文字和背景图的RGB差距总是大于一个固定的阈值,即使每次图片的文字和背景颜色都会变化,比如:

    新浪和discuz的验证码

    20130607213048_17172.jpg20130607213105_77426.jpg

    通过对文字部分和干扰部分取样可以发现,文字部分的R、G值一般在100左右,B值接近255,但是背景干扰的R、G值则大大高于文字部分,接近200,比较接近文字轮廓部分的像素的RG值也在150以上。通过程序遍历一遍像素就可以完全去掉背景。

    20130607213146_11252.jpg

    Discuz的验证码同理

    对于一些和文字颜色相同但是较为分散和单一的干扰像素点,我们可以用判断相邻像素的方法,对于每个点判断该点和相邻8个点的色差,若色差大于某个值,则+1,如果周围有超过6个点的色差都比较大,说明这个点是噪点。对于图像边界的一圈像素,周围没有8个像素,则统统清除,反正文字都在图片的中间位置。

    如下图:假如当前像素的坐标是x,y  图形坐标系的原点是图像的左上角

    20130607213238_98076.jpg

    干扰线对于识别验证码增加了一些难度,不过干扰线只有很小的几率会以大角度曲线的方式出现,大部分时间还是小角度直线,去除算法可以参考http://wenku.baidu.com/view/63bac64f2b160b4e767fcfed.html

    对于1个像素粗细的干扰线,在字符为2个像素以上的时候,可以用去噪点算法作为滤镜,多执行几次,就可以完美的把细干扰线去掉。

    对于像素数比干扰点稍大的干扰色块,可以采用的算法有:

    油漆桶算法(又叫种子填充算法,Floodfill)

    种子填充算法可以方便的计算出任意色块的面积,对于没有粘连字符或者粘连但是字符每个颜色不一样的验证码来说,去除干扰色块的效果很好,你只需要大概计算一下最小的和最大的字符平均占多少像素,然后把这段区间之外像素数的色块排除掉即可。

    Recursive_Flood_Fill_4_%28aka%29.gif                    Recursive_Flood_Fill_8_%28aka%29.gif

    上下左右4个方向填充还有8个方向填充的不同

    判断颜色分布:

    对于大多数彩色验证码来说,文字基本在图片中心的位置,每个字符本身的颜色是一样的,也就是说对于文字来说,同一种颜色基本都集中在一个固定的区域范围内,通过统计图片中的像素,按近似颜色分组,同时分析每个颜色组在图片中的分布范围,假如说有一种颜色大部分像素都在图片边缘,那么这个颜色肯定不属于要识别的字符,可以去掉。

    对于干扰线,并没有一种十分有效的方式能完全去除并且不影响到文字,不过如果能够成功分割字符的话,少量干扰线对于识别率影响不大。

    字符分割算法

    破解验证码的重点和难点就在于能否成功分割字符,这一点也是机器视觉里的一道难题,对物件的识别能力。对于颜色相同又完全粘连的字符,比如google的验证码,目前是没法做到5%以上的识别率的。不过google的验证码基本上人类也只有30%的识别率

    对于字符之间完全没有粘连的验证码,比如这个->_-> captcha.php

    分割起来是非常的容易,用最基本的扫描线法就可以分割,比如从最左侧开始从上到下(y=0---|||||y=n)扫描,如果没有遇到任何文字的像素,就则往右一个像素然后再扫描,如果遇到有文字像素存在,就记录当前横坐标,继续向右扫,突然没有文字像素的时候,就说明到了两个字符直接的空白部分,重复这个步骤再横向扫描就能找到每个字符最边缘4个像素的位置,然后可以用PIL内建的crop功能把单独的字符抠出来。

    对于有少许粘连但是只是在字符边角的地方重叠几个像素的验证码,可以用垂直像素直方图的统计方法分割。如下图:

    20130607230457_19520.png

    图上半部分是垂直像素直方图的一种直观展示,假如图片宽度为100像素,则把图片切割为100个1像素的竖线,下面的红色部分为当前x坐标上所有黑色像素的总和。这么一来可以很容易的通过直方图的波峰波谷把4个字母分割开。图片的下半部分是扫描线分隔法,因为干扰线和字符旋转的存在,只有M和5直接才出现了连续的空白部分。

    除了垂直像素直方图,还可以从不同的角度进行斜线方向的像素数投影,这种方式对于每次全体字符都随机向一个角度旋转的验证码效果很好。对于每次字符大小和数量都一样的验证码还可以用平均分割法,也就是直接先把中间的文字部分整体切出来,然后按宽度平均分成几份,这种方式对字符粘连比较多用其他方式不好分割的验证码很有用,之前的megaupload的3位字母验证码就是通过这种方式成功分割的。

    另外对于彩色的验证码,还可以用颜色分割,比如12306的:

    20130607233647_17982.png

    12306的验证码,每个字符颜色都不一样,真是省事啊。

    作为验证码识别里的难点,分割字符还有很多种算法,包括笔画分析曲线角度分析等等,不过即便如此,对粘连的比较厉害的字符还是很难成功的。

    标准化

    标准化的意思是指对于同一个字符,尽可能让每次识别前的样本都一致,以提高识别率。而验证码设计者则会用随机旋转,随机扭曲还有随机字体大小的方式防止字符被简单方法识别。

    还原随机旋转的字符一般采用的是旋转卡壳算法:

    20130607235719_94258.png

    此算法非常简单,对一张图片左右各旋转30度的范围,每次1度,旋转后用扫描线法判断字符的宽度,对于标准的长方形字体,在完全垂直的时候肯定是宽度最窄的。嗯?纳尼?上面的图是中间的最窄?好像的确是这样,不过只要每次旋转后的结果都一样,对于识别率不会有影响。

    扭曲还原的算法比较蛋疼,效果也不怎么样(其实我不会),不过如果识别算法好的话,对扭曲的字符只要人能认出来,识别率也可以达到接近人类的水准。

    还有一些常用到的算法,对于提高识别率和减少样本数量有一定帮助:

    骨架细化:腐蚀算法

    20130608000722_87311.png

    腐蚀算法的原理有点像剥洋葱,从最外层沿着最外面的一层像素一圈一圈的去掉,直到里面只剩下一层像素为止。腐蚀算法里面需要用到另一个算法,叫做凸包算法,用来找一堆像素点里面最外围的一层。

    最后就是把字符变成统一大小,一般而言是把全部字符都缩到和验证码里出现过的最小的字符一个大小。

    详情请自行google……

    20130608001005_74310.png

    分割算法差不多就到这里了,都是一些比较基础的内容。下面是最终的识别。

    0x02 识别

    其实到了这一步,单独的字符已经分离出来了,可以训练tesseract ocr来识别了,样本数量多的话,识别率也是很高的。不过在这里还是要讲一下,如何自己来实现识别过程。

    第一步,样本现在应该已经是一个矩阵的形式了,有像素的地方是1,背景是0,先肉眼识别一下,然后把这个矩阵转换为字符串,建立一个键值对,标明这串字符串是什么字符。之后就只需要多搜集几个同样字符的不同字符串变形,这就是制作模板的过程,。

    搜集了足够多的模板后,就可以开始识别了,最简单的方法:汉明距离,但是如果字符有少许扭曲的话,识别率会低的离谱。对比近似字符串用的最多一般是 编辑距离算法(Levenshtein Distance),具体请自己google。

    两种算法的差别在于,对同样两个字符串对比10010101和10101010,汉明距离是6,但是编辑距离是2。

    最后一种最NB的识别算法,就是神经网络,神经网络是一种模拟动物神经元工作模式的算法,神经网络有多种不同的结构,但是基本架构分为输入层,隐含层和输出层,输入和输出均为二进制。

    20130608003739_59697.png

    对于验证码识别来说,输入和输出节点不宜过多,因为多了很慢……所以如果样本矩阵为20x20 400个像素的话,需要对应的也要有400个输入节点,因此我们需要对整个矩阵提取特征值,比如先横向每两个数字XOR一下,然后再竖向每两个数字XOR。

    Python有很多封装好的神经网络库,你所需要的只是把特征值输入神经网络,再告诉他你给他的是什么(字符),这样多喂几次之后,也就是训练的过程,随着训练的进行,神经网络的内部结构会改变,逐渐向正确的答案靠拢。神经网络的优势是,对于扭曲的字符识别成功率非常高。另外神经网络在信息安全中还可以起到很多其他作用,比如识别恶意代码等等。

    动画验证码

    有些不甘寂寞的程序员又玩出了些新花样,比如各种GIF甚至flv格式的动画验证码,下面我来分析一下腾讯安全中心的GIF验证码。

    20130608005708_49515.gif

    晃来晃去的看似很难,放慢100倍一帧一帧再看看?

    20130608010202_83349.gif

    基本上每帧都有一个字符和其他的分开,用最简单的扫描法就能分割出来。

    剩下的就很轻松了,旋转还原之后,先填充内部空白,缩小细化之后做成模板对比,识别率怎么也得有90%了。

    原本一张图就能搞定的事情,偏偏给了我们8张图,而且每张图还有一点区别,平白无故增大了很多信息量。

    另外就是一些所谓的高用户体验的验证码,比如freebuf的:

    20130608010939_57828.png

    拖动解锁按钮会触发执行一段js,生成一串随机字符串,ajax给后端程序判断。

    破解方式就当留给大家的思考题了,假如我想刷评论的话,怎么办。

    还有就是声音验证码的识别,现在很多验证码为了提高用户体验和照顾视觉障碍的用户,都有声音验证码,一般来说是机器生成一段读数字的语音。但是在这方面上很多程序员都偷懒了,预先找了10个数字的声音录音,然后生成的时候把他们随机拼到一起,结果就是这样:

    20130608011512_96225.png

    前3秒为语音提示,后面的是数字,有没有发现什么?

    声音也是可以做成模板的哦

    最后就是应该怎么样去设计验证码

    •整体效果
    
    
    •字符数量一定范围内随机
    
    
    •字体大小一定范围内随机
    
    
    •波浪扭曲(角度方向一定范围内随机)
    
    
    •防识别
    
    
    •不要过度依赖防识别技术
    
    
    •不要使用过多字符集-用户体验差
    
    
    •防分割 
    
    
    •重叠粘连比干扰线效果好
    
    
    •备用计划
    
    
    •同样强度完全不同的一套验证码
    

    附件添加一个破解验证码的实例包括程序大家自行研究吧:验证码识别

    ©乌云知识库版权所有 未经许可 禁止转载
    展开全文
  • 用过12306的魔性验证码之后,一直对验证码有种厌恶,感觉验证码就是我完成目标的绊脚石。 但是前段时间在做一个项目时,有一天凌晨网站被黑客攻击,第二天上班被告知,给用户发短信的好几万花完了!哪里出问题了!...

    UED1

    用过12306的魔性验证码之后,一直对验证码有种厌恶,感觉验证码就是我完成目标的绊脚石。

    但是前段时间在做一个项目时,有一天凌晨网站被黑客攻击,第二天上班被告知,给用户发短信的好几万花完了!哪里出问题了!?

    原来是黑客在我们的注册页写了一个不断填写手机号获取手机验证码的脚本。获取手机验证码之前,注册页没有让用户填写验证码,不能判断填写的是人还是机器。这个脚本在短时间内不断循环,不断请求手机验证码,服务端不断发短信,短短几十分钟,短信发完,钱没了。

    在这件事情发生之后,我才开始意识到,原来那么讨人厌的验证码却起着大大的作用。于是我就开始思考验证码是什么,用在哪,有什么用处。

     

    一、验证码是什么

     

    验证码通过让用户输入图中内容或做某种操作,区分用户是人还是机器人。验证码能有效防止机器人进行身份欺骗,对服务器进行攻击或写入垃圾数据,是种安全且成本低的验证方法。

     

    二、验证码用在哪

     

    1.登录

    登录时验证,可防止机器人不断输入无效的账号密码进行登录,导致服务器访问压力过大。

    为了保证用户体验,目前大部分网站首次登录时都无需验证的,要是用户登录多次后仍没登录成功,此时需要用户验证自己是人还是机器。

    比如,刚打开的JD登录界面:

    1

    登录多次不成功后,需要验证:

    2

     

    2.注册

    现在注册已经不像很久以前填几个数据就能完成注册了。

    现在注册需要做身份的唯一验证,比如绑定手机号/邮箱/微信号/QQ号等

    添加身份验证可以有效防止一个用户注册多个帐号的情况,如果一个用户注册多个账户会导致系统存有太多无效账户,增加服务器压力。

    注册验证有多种,目前比较常见的:

     

    1)邮箱验证

    邮箱验证一般是提交注册信息后,会显示通过邮件激活帐号。打开邮箱点击激活链接后,帐号才算注册成功。

     

    2)手机验证

    手机验证一般是输入手机号,然后给该手机号发送验证码。

    若是验证码通过短信发送,则需要在发送短信验证码前,让用户验证人机,确定用户是人之后再给该手机发送短信验证码。若不加以识别会让不法分子有机可乘,写个脚本在你的注册页面不断获取短信验证码,导致服务器压力大,把发短信的钱花完。导致正常用户不能访问或获取不到验证码。

    如下图,用手机注册,点击’发送验证码’后,屏幕会弹出一个验证,验证人机,验证成功后才给该手机号发送验证码。

    3

     

    3.评论回复

    内容主导型产品大多有评论功能,但是如果评论功能不加以限制,很快有会有不法份子在产品内发布多条无效消息或不良广告。

    根据平台属性,对评论会有三种限制方式。

     

    1)论坛型产品,如新浪论坛评论,注重论坛活跃度,允许用户发表多条评论,此时一般会用到身份验证辨别人机。

    4

     

    2)知识性产品,如知乎,内容导向型产品,注重UGC的质量,会限制回复次数。

    5

     

    3)微信订阅号,对文章进行评论,需要公众号管理员筛选后才能展现在评论区,也是保证内容质量的一种方法。

    6

     

    三、验证码类别

     

    1.输入

    现在4个数字的验证码很容易用图像识别识破。所以需要有多种形式的验证码。

     

    1)更多字符的验证码:

    7

     

    2)运算验证码:

    8

     

    3)问题验证码:

    9

     

    2.滑动

    通过滑动条把区块拖动到某个地方上:

    10

     

    3.点击选择

    根据文字提示,进行选择。

     

    1)如根据文字选图:

    11

     

    2)根据提示文字进行操作:

    12

     

    还有很多各种各样的验证码,本文只展示小部分~

     

     

    四、验证码可以有其它方法替代吗

     

    1.二维码验证

    1)目前很多网站都采用二维码登录。因为现在移动端市场份额比pc端大得多,移动端APP一般默认登录状态,扫码入口也突出,通过扫码能验证用户身份唯一性,不需要用户填写登录信息,又能识别人机,是个快捷安全的方式。

    13

    二维码具有时效性,要是长时间不登录,二维码会失效:

    14

     

    2)同样二维码也可以用作注册。

    大部分pc端网站用微信或QQ注册,会让用户扫二维码注册,但扫完之后再让用户填写注册信息,这么做等于绑定了用户的某个社交产品账户。

    有的对账户信息没那么重要的产品,也会扫码立马完成注册的,后面再补充的个人信息,也能防止机器注册。

    15

     

    2.指纹验证

    指纹验证比较普遍,常见于金融类产品,可用于开启锁屏或登录/支付验证。常用于验证是否本人,也能有效分辨人机。

    16

     

    3.刷脸识别

    刷脸识别目前还不普遍,用于验证用户身份,也能有效分辨人机。

    现在智能手机基本都有前置摄像头,刷脸能免去用户输入的操作,比较便捷,常用于开启手机锁屏,登录验证等。

    17

     

    免输入是验证的趋势,后面估计会有更多通过识别生理特征免输入的验证方法,能达到快速验证身份的效果。

    pc端的验证方法有多样,推荐用能让用户快速完成的验证方法,不然太难的验证方法会让用户产生挫败感,对你产品的印象大打折扣。

    通过验证,识别人机能大大减少机器给产品注入的无效数据,保障产品数据的有效性,减少不必要的损失。

    展开全文
  • 验证码操作在开发中是经常会使用到的,这里我为大家提供三种生成验证码方式,分别是:(1)jsp页面中直接生成验证码、(2)使用Servlet生成验证码、(3)、在struts2中实现验证码操作。下面就来看一下它们都是怎样...

    验证码操作在开发中是经常会使用到的,这里我为大家提供三种生成验证码方式,分别是:(1)jsp页面中直接生成验证码、(2)使用Servlet生成验证码、(3)、在struts2中实现验证码操作。下面就来看一下它们都是怎样实现的,这里提供完整的代码,并有详细的注释说明。

    (1)、在jsp页面中直接生成验证码

    //image.jsp
    <%@ page contentType="image/jpeg"
    	import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*"
    	pageEncoding="GBK"%>
    <%!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);
    	}%>
    <%
    	//设置页面不缓存
    	response.setHeader("Pragma", "No-cache");
    	response.setHeader("Cache-Control", "no-cache");
    	response.setDateHeader("Expires", 0);
    
    	// 在内存中创建图象
    	// 通过这里可以修改图片大小
    	int width = 85, height = 23;
    	BufferedImage image = new BufferedImage(width, height,
    			BufferedImage.TYPE_INT_RGB);
    
    	// 获取图形上下文
    	// g相当于笔
    	Graphics g = image.getGraphics();
    
    	//生成随机类
    	Random random = new Random();
    
    	// 设定背景色
    	g.setColor(getRandColor(200, 250));
    	// 画一个实心的长方,作为北京
    	g.fillRect(0, 0, width, height);
    
    	//设定字体
    	g.setFont(new Font("黑体", Font.PLAIN, 18));
    
    	//画边框
    	g.setColor(Color.BLUE);
    	g.drawRect(0,0,width-1,height-1);
    
    	// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
    	g.setColor(getRandColor(160, 200));
    	for (int i = 0; i < 155; i++) {
    		int x = random.nextInt(width);
    		int y = random.nextInt(height);
    		int xl = random.nextInt(12);
    		int yl = random.nextInt(12);
    		g.drawLine(x, y, x + xl, y + yl);
    	}
    
    	// 取随机产生的认证码(4位数字)
    	//String rand = request.getParameter("rand");
    	//rand = rand.substring(0,rand.indexOf("."));
    	String sRand = "";
    	// 如果要使用中文,必须定义字库,可以使用数组进行定义
    	// 这里直接写中文会出乱码,必须将中文转换为unicode编码
    	String[] str = { "A", "B", "C", "D", "E", "F", "G", "H", "J", "K",
    			"L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
    			"Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
    			"k", "m", "n", "p", "s", "t", "u", "v", "w", "x", "y", "z",
    			"1", "2", "3", "4", "5", "6", "7", "8", "9" };
    
    	for (int i = 0; i < 5; i++) {
    		String rand = str[random.nextInt(str.length)];
    		sRand += rand;
    		// 将认证码显示到图象中
    		g.setColor(new Color(20 + random.nextInt(110), 20 + random
    				.nextInt(110), 20 + random.nextInt(110)));//调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
    		g.drawString(rand, 16 * i + 6, 19);
    	}
    
    	// 将认证码存入SESSION
    	session.setAttribute("rand", sRand);
    
    	// 图象生效
    	g.dispose();
    
    	// 输出图象到页面
    	ImageIO.write(image, "JPEG", response.getOutputStream());
    	out.clear();
    	out = pageContext.pushBody();
    %>
    
    //使用验证码的页面login.jsp
    <%@ page contentType="text/html" pageEncoding="GBK"%>
    <html>
    	<head>
    		<title>登陆页面</title>
    		<script>
    	function reloadImage() { 
    		document.getElementById('identity').src = 'image.jsp?ts=' + new Date()
    				.getTime();
    	}
    </script>
    	</head>
    	<body>
    		<center>
    			<%
    				// 乱码解决
    				request.setCharacterEncoding("GBK");
    			%>
    			<h1>
    				登陆程序
    			</h1>
    			<hr>
    			<%=request.getAttribute("info") != null ? request
    					.getAttribute("info") : ""%>
    			<form action="check.jsp" method="post">
    				用户ID:
    				<input type="text" name="mid">
    				<br>
    				密码:
    				<input type="password" name="password">
    				<br>
    				验证码:
    				<input type="text" name="code"  maxlength="5" size="5">
    				<img src="image.jsp" id="identity" οnclick="reloadImage()" title="看不清,点击换一张">
    				<br>
    				<input type="submit" value="登陆">
    				<input type="reset" value="重置">
    			</form>
    		</center>
    	</body>
    </html>

    效果如下:

    (2)、使用Servlet生成验证码

    //IdentityServlet.java代码如下:
    package com.helloweenvsfei.servlet;
    
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics2D;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Random;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.sun.image.codec.jpeg.JPEGCodec;
    import com.sun.image.codec.jpeg.JPEGImageEncoder;
    
    public class IdentityServlet extends HttpServlet {
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = -479885884254942306L;
    
    	public static final char[] CHARS = { '2', '3', '4', '5', '6', '7', '8',
    			'9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
    			'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
    
    	public static Random random = new Random();
    
    	public static String getRandomString() {
    		StringBuffer buffer = new StringBuffer();
    		for (int i = 0; i < 6; i++) {
    			buffer.append(CHARS[random.nextInt(CHARS.length)]);
    		}
    		return buffer.toString();
    	}
    
    	public static Color getRandomColor() {
    		return new Color(random.nextInt(255), random.nextInt(255), random
    				.nextInt(255));
    	}
    
    	public static Color getReverseColor(Color c) {
    		return new Color(255 - c.getRed(), 255 - c.getGreen(), 255 - c
    				.getBlue());
    	}
    
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    
    		response.setContentType("image/jpeg");
    
    		String randomString = getRandomString();
    		request.getSession(true).setAttribute("randomString", randomString);
    
    		int width = 100;
    		int height = 30;
    
    		Color color = getRandomColor();
    		Color reverse = getReverseColor(color);
    
    		BufferedImage bi = new BufferedImage(width, height,
    				BufferedImage.TYPE_INT_RGB);
    		Graphics2D g = bi.createGraphics();
    		g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
    		g.setColor(color);
    		g.fillRect(0, 0, width, height);
    		g.setColor(reverse);
    		g.drawString(randomString, 18, 20);
    		for (int i = 0, n = random.nextInt(100); i < n; i++) {
    			g.drawRect(random.nextInt(width), random.nextInt(height), 1, 1);
    		}
    
    		// 转成JPEG格式
    		ServletOutputStream out = response.getOutputStream();
    		JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
    		encoder.encode(bi);
    		out.flush();
    	}
    
    	public static void main(String[] args) {
    		System.out.println(getRandomString());
    	}
    }
    //Web.xml的配置为:
    <servlet>
        <servlet-name>IdentityServlet</servlet-name>
        <servlet-class>com.helloweenvsfei.servlet.IdentityServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>IdentityServlet</servlet-name>
        <url-pattern>/servlet/IdentityServlet</url-pattern>
    </servlet-mapping>
    
    //测试页面identity.html为:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    	<head>
    		<title>identity.html</title>
    
    		<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    		<meta http-equiv="description" content="this is my page">
    		<meta http-equiv="content-type" content="text/html; charset=GB18030">
    
    		<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
    
    	</head>
    
    	<body>
    
    <script>
    	function reloadImage() {
    		document.getElementById('btn').disabled = true;
    		document.getElementById('identity').src='servlet/IdentityServlet?ts=' + new Date().getTime();
    	}
    	</script>
    
    		<img src="servlet/IdentityServlet" id="identity" οnlοad="btn.disabled = false; " />
    		<input type=button value=" 换个图片 " οnclick="reloadImage()" id="btn">
    
    	</body>
    </html>
    
    

    (3)、在Struts2应用中生成验证码

    //RandomNumUtil.java
    package org.ml.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.util.Random;
    import javax.imageio.ImageIO;
    import javax.imageio.stream.ImageOutputStream;
    
    public class RandomNumUtil {
    	public static final char[] CHARS = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
    		'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z','2', '3', '4', '5', '6', '7', '8',
    		'9','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
    		'n', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; 
    	private ByteArrayInputStream image;// 图像
    	private String str;// 验证码
    	/**
    	 *  构造方法调用初始化属性方法
    	 */
    	private RandomNumUtil() {
    		init();
    	}
    
    	/**
    	 * 取得RandomNumUtil实例
    	 */
    	public static RandomNumUtil Instance() {
    		return new RandomNumUtil();
    	}
    
    	/**
    	 * 取得验证码图片
    	 */
    	public ByteArrayInputStream getImage() {
    		return this.image;
    	}
    
    	/**
    	 * 取得图片的验证码
    	 */
    	public String getString() {
    		return this.str;
    	}
    	
    	/**
    	 * 初始化属性否具体方法
    	 */
    	private void init() {
    		// 在内存中创建图象
    		int width = 85, height = 18;
    		//设置图形的高度和宽度,以及RGB类型
    		BufferedImage image = new BufferedImage(width, height,
    				BufferedImage.TYPE_INT_RGB);
    		// 获取图形上下文
    		Graphics g = image.getGraphics();
    		// 生成随机类
    		Random random = new Random();
    		// 设定背景色
    		g.setColor(getRandColor(200, 250));
    		g.fillRect(0, 0, width, height);
    		// 设定字体
    		g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
    		// 随机产生255条干扰线,使图象中的认证码不易被其它程序探测到
    		g.setColor(getRandColor(160, 200));
    		for (int i = 0; i < 255; i++) {
    			int x = random.nextInt(width);
    			int y = random.nextInt(height);
    			int xl = random.nextInt(12);
    			int yl = random.nextInt(12);
    			g.drawLine(x, y, x + xl, y + yl);
    		}
    		// 取随机产生的认证码(6位数字)
    		StringBuffer sRand = new StringBuffer();  
    		for (int i = 0; i < 6; i++) {
    			String rand = String.valueOf(CHARS[random.nextInt(CHARS.length-1)]);//从字符数组中随机产生一个字符
    			sRand.append(rand); 
    			// 将认证码显示到图象中
    			g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
    			// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
    			g.drawString(rand, 13 * i + 6, 17);
    		}
    		// 赋值验证码
    		this.str = sRand.toString();
    
    		// 图象生效
    		g.dispose();
    		//下面将生成的图形转变为图片
    		ByteArrayOutputStream output = new ByteArrayOutputStream(); 
    		ByteArrayInputStream input = null;
    		try {
    			ImageOutputStream imageOut = ImageIO.createImageOutputStream(output);
    			ImageIO.write(image, "JPEG", imageOut);//将图像按JPEG格式写入到imageOut中,即存入到output的字节流中
    			imageOut.close();//关闭写入流
    			input = new ByteArrayInputStream(output.toByteArray());//input读取output中的图像信息
    		} catch (Exception e) {
    			System.out.println("验证码图片产生出现错误:" + e.toString());
    		}
    
    		this.image = input;/* 赋值图像 */
    	} 
    	/*
    	 * 给定范围获得随机颜色
    	 */
    	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);
    	}
    }
    
    //RandomAction.java的代码:
    package org.ml.action;
    
    import java.io.ByteArrayInputStream;
    
    import org.ml.util.RandomNumUtil;
    
    import com.opensymphony.xwork2.ActionContext;
    import com.opensymphony.xwork2.ActionSupport;
    
    @SuppressWarnings("serial")
    public class RandomAction extends ActionSupport {
    	private ByteArrayInputStream inputStream;
    
    	public String execute() throws Exception {
    		RandomNumUtil rdnu = RandomNumUtil.Instance();//取得随机验证码产生类的对象
    		this.setInputStream(rdnu.getImage());// 取得带有随机字符串的图片
    		ActionContext.getContext().getSession().put("random", rdnu.getString());// 取得随机字符串放入HttpSession
    		return SUCCESS;
    	}
    
    	public void setInputStream(ByteArrayInputStream inputStream) {
    		this.inputStream = inputStream;
    	}
    
    	public ByteArrayInputStream getInputStream() {
    		return inputStream;
    	}
    }
    //struts.xml配置为:
    <!-- Random验证码 -->
    <action name="rand" class="org.ml.action.RandomAction">
        <result type="stream" name="success">
    	<param name="contentType">image/JPEG</param>
    	<param name="inputName">inputStream</param>
        </result>
    </action>
    //HTML中的表单代码为:
    <tr height="35" >
    <td width="14%" class="top_hui_text">
    <span class="login_txt"> 验证码: &nbsp;&nbsp; </span>
    </td>
    <td colspan="2" class="top_hui_text">
    <input type="text" name="rand" id="rand" size="6"
    maxlength="6">
     <script type="text/javascript"> 
    function changeValidateCode(obj) { 
    //获取当前的时间作为参数,无具体意义 
    var timenow = new Date().getTime(); 
    //每次请求需要一个不同的参数,否则可能会返回同样的验证码 
    //这和浏览器的缓存机制有关系,也可以把页面设置为不缓存,这样就不用这个参数了。 
    obj.src="rand.action?d="+timenow; 
    } 
     </script>
    <img src="rand.action" title="点击图片刷新验证码"
    οnclick="changeValidateCode(this)" height="22"
    width="80" />
    </td> 
    </tr>

    以上使用了三种方式进行验证码的生成,如果需要的话,可以仿照这个来进行开发。

    展开全文
  • 知道么?如何使用java生成图片验证码——补充KaptchaServlet验证码的使用

    验证码的作用


    一种区分用户是计算机还是人的公共全自动程序,可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式 ——百科解释

    验证码生成步骤


    1.在内存中生成一张图片
    2.生成随机的四个字母或者数字
    3.将随机产生的字符或数字写入到图片上
    4.增加旋转,干扰线等
    5.显示到页面

    API介绍

    • BufferedImage
      Image是一个抽象类,BufferedImage是其实现类,是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中(BufferedImage生成的图片在内存里有一个图像缓冲区,利用这个缓冲区我们可以很方便地操作这个图片)

    • Graphics
      简单的来说是操作图像的画笔对象,能够绘制图像边框,背景色,写入字符串,绘制直线等等

    • Graphics2D
      该Graphics2D类继承自Graphics类,以提供对几何,坐标变换,颜色管理和文本布局的更复杂的控制。 这是在Java(tm)平台上呈现二维形状,文字和图像的基础类,父类有的方法它都可以使用(这不是废话么 --!)

    • ImageIO
      同过read 和write 方法,将图片读取到内存中,或者将内存中的图片由输出流输出

      String imgPath = "D://a.jpg"; 
      BufferedImage image = ImageIO.read(new FileInputStream(imgPath));
      

      将图片读取内存中,只有读取到内存中才可以操作

        	ImageIO.write(bufferedImage, "jpg", resp.getOutputStream());
      

      将内存中的图片通过响应输出到前台

    前端demo

    <body>
    	<form action="/login" method="POST">
    		验证码:<input type="text" name="checkcode" /><img id="img" onclick="changeImg()"
    			src="${pageContext.request.contextPath}/ImgCheck"><br> <input
    			type="submit" value="提交" />
    
    	</form>
    </body>
    
    <script type="text/javascript">
     function changeImg(){
    	 var img =document.getElementById("img");
    	 //相同的请求,浏览器会将图片缓存下来,需要加一个参数,让每次请求都不一样,每次得到新的图片
    	 img.src="${pageContext.request.contextPath}/ImgCheck?time="+new Date().getTime();
     }
    

    通过提交将代码将代码提交到/login对应的servlet里面

    java实现生成图片验证码


    /ImgCheck请求的servlet如下:

    @WebServlet(urlPatterns = "/ImgCheck")
    public class ImgCheckServlet extends HttpServlet {
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		int width = 120;
    		int height = 30;
    		// 1.在内存中生成一个图像
    		BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    		// 2.操作图片,设置背景,边框,倾斜
    		// 获得画笔对象
    		Graphics graphics = bufferedImage.getGraphics();
    		// 设置图片背景色  需要说明单的rgb颜色数值越大,颜色越浅,颜色值越小,颜色越深,背景色需要浅色
    		graphics.setColor(getRandColor(220, 250));
    		// 填充一个矩形 坐标和长宽
    		graphics.fillRect(0, 0, width, height);
    		// 绘制边框
    		graphics.setColor(Color.blue);
    		graphics.drawRect(0, 0, width - 1, height - 1);
    		// 3.生成随机的4个数字或者图片写入到图片里
    		// 把父类对象转为子类,图形功能扩展	
    		Random random = new Random();
    		Graphics2D g2d = (Graphics2D) graphics;
    		// 设置字体的颜色随机  字体需要颜色较深
    		g2d.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
    		// 字体,样式,大小
    		g2d.setFont(new Font("宋体", Font.BOLD, 18));
    		String words = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789";
            //将产生的code保存到session中,request在每次请求图片中,请求结束就消失 了,不能使用request域
    		StringBuffer sb =new StringBuffer();
    		int x = 10;
    		for (int i = 0; i < 4; i++) {
    			int index = random.nextInt(words.length());// 返回0到长度的随机数,不含右边界
    			char ch = words.charAt(index);
    			sb.append(ch);
    			// 每个字符选左右旋转30度之间
    			int jiaodu = random.nextInt(60) - 30;// 产生-30 ~ 30之间
    			// 角度转弧度
    			double hudu = jiaodu * Math.PI / 180;
    			// 旋转一定的弧度
    			g2d.rotate(hudu, x, 20);
    			// graphics画图的坐标是从左上角开始算的x 横坐标 ,y表示字符的基线的位置
    			g2d.drawString(String.valueOf(ch), x, 20);
    			// 每次画完图之后再旋转回来
    			g2d.rotate(-hudu, x, 20);
    			x += 30;
    		}
    		//将code存在session中,在登录servlet中就能取到该值和界面传去的验证码作比较
    		req.getSession().setAttribute("checkcode", sb.toString());
    		
    		//4.绘制图片的干扰线  干扰线的颜色介于两者之间
    		graphics.setColor(getRandColor(160, 200));
    		int x1,x2,y1,y2;
    		//30条干扰线
    		for(int i=0;i<30;i++){
    			x1 = random.nextInt(width-12);
    			x2 = random.nextInt(12);
    			y1 = random.nextInt(height-12);
    			y2 = random.nextInt(12);
    			//在此图形上下文的坐标系中的点(x1,y1)和(x2,y2)之间绘制一条线。
    			graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
    		}
    		//释放资源
    		graphics.dispose();
    		// 5.输出内存中的图片
    		ImageIO.write(bufferedImage, "jpg", resp.getOutputStream());
    
    	}
    

    随机获取某一范围内的颜色rgb(r,g,b)

    	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);
    	}
    

    验证表单时,校验验证码的争取性,验证码错误则还是到当前页面,验证码正确才能继续向下执行

    @WebServlet("/login")
    public class LoginServlet extends HttpServlet {
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		// 完成验证码的校验:
    		// 获得session中保存的验证码的信息
    		String code1 = (String)request.getSession().getAttribute("checkcode");
    		// 接收前台表单提交的验证码的信息
    		String code2 = request.getParameter("checkCode");
    		if(code2==null || !code2.equalsIgnoreCase(code1)){
    			request.setAttribute("msg", "验证码输入不正确!");
    			request.getRequestDispatcher("/login.jsp").forward(request, response);
    			return;
    		}
    		...
    

    最终实现的效果如下:
    在这里插入图片描述
    如果觉得上方的验证码有需求改动每次还要改动代码,这里可以使用使用第三方的验证码校验工具Kaptcha验证码,它实现的功能主要是实现了上面的配置ImgCheckServlet

    Kaptcha验证码

    • 需要引入jar包:kaptcha-2.3.2.jar ,查看源代码可以看 kaptcha-2.3.2-sources.jar
    • 配置web.xml
      配置如下:
     <servlet>
     	<servlet-name>KaptchaServlet</servlet-name>
     	<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
     
     	<!-- 修改随机的字母或数字的长度 -->
     	<init-param>
     		<param-name>kaptcha.textproducer.char.length</param-name>
     		<param-value>4</param-value>
     	</init-param>
     	
     	<!-- 修改字体大小 -->
     	<init-param>
     		<param-name>kaptcha.textproducer.font.size</param-name>
     		<param-value>25</param-value>
     	</init-param>
     	<init-param>
     		<param-name>kaptcha.textproducer.char.space</param-name>
     		<param-value>10</param-value>
     	</init-param>
     	
     	<!-- 修改图片的宽高 -->
     	<init-param>
     		<param-name>kaptcha.image.width</param-name>
     		<param-value>120</param-value>
     	</init-param>
     	<init-param>
     		<param-name>kaptcha.image.height</param-name>
     		<param-value>30</param-value>
     	</init-param>
     </servlet>
     
     <servlet-mapping>
     	<servlet-name>KaptchaServlet</servlet-name>
     	<url-pattern>/KaptchaServlet</url-pattern>
     </servlet-mapping>
    
    • 其中配置servlet参数有如下介绍:

      KaptchaServlet类的源码

    private Properties props = new Properties();
    

    定义一个props来保存键值对,加载配置信息
    再来看看serverlet 初始化的时候init方法

    	@Override
    	public void init(ServletConfig conf) throws ServletException
    	{
    		super.init(conf);
    
    		// Switch off disk based caching.
    		ImageIO.setUseCache(false);
            //从web.xml中读取所有的配置信息
    		Enumeration<?> initParams = conf.getInitParameterNames();
    		while (initParams.hasMoreElements())
    		{
    			String key = (String) initParams.nextElement();
    			String value = conf.getInitParameter(key);
    			//将每一个配置信息的key - value 放到 props里面
    			this.props.put(key, value);
    		}
          // 通过 Config 对象加载这些配置
    		Config config = new Config(this.props);
    		this.kaptchaProducer = config.getProducerImpl();
    		this.sessionKeyValue = config.getSessionKey();
    		this.sessionKeyDateValue = config.getSessionDate();
    	}
    

    再看它的doget()方法

    	@Override
    	public void doGet(HttpServletRequest req, HttpServletResponse resp)
    			throws ServletException, IOException
    	{
    		// Set to expire far in the past.
    		resp.setDateHeader("Expires", 0);
    		// Set standard HTTP/1.1 no-cache headers.
    		resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
    		// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
    		resp.addHeader("Cache-Control", "post-check=0, pre-check=0");
    		// Set standard HTTP/1.0 no-cache header.
    		resp.setHeader("Pragma", "no-cache");
    
    		// return a jpeg
    		resp.setContentType("image/jpeg");
    
    		// create the text for the image  创建文本
    		String capText = this.kaptchaProducer.createText();
    
    		// store the text in the session 将文本放入session
    		req.getSession().setAttribute(this.sessionKeyValue, capText);
    
    		// store the date in the session so that it can be compared
    		// against to make sure someone hasn't taken too long to enter
    		// their kaptcha
    		req.getSession().setAttribute(this.sessionKeyDateValue, new Date());
    
    		// create the image with the text   使用该文本创建内存中的图片
    		BufferedImage bi = this.kaptchaProducer.createImage(capText);
    
    		ServletOutputStream out = resp.getOutputStream();
    
    		// write the data out  将该图片输出到jsp
    		ImageIO.write(bi, "jpg", out);
    	}
    

    Config类

    用来加载配置

    public class Config
    {
    	/** */
    	private Properties properties;
    
    	/** */
    	private ConfigHelper helper;
    
    	/** */
    	public Config(Properties properties)
    	{
    		this.properties = properties;
    		this.helper = new ConfigHelper();
    	}
    
    	/** */
    	public boolean isBorderDrawn()
    	{
    	  /
    		String paramName = Constants.KAPTCHA_BORDER;
    		String paramValue = this.properties.getProperty(paramName);
    		return this.helper.getBoolean(paramName, paramValue, true);
    	}
    

    Constants类


    定义了一些属性的名字,如果需要修改默认配置,需要知道这写name的含义
    如下:

    public class Constants {
    
    	public Constants() {
    	}
       //
    	public static final String KAPTCHA_SESSION_KEY = "KAPTCHA_SESSION_KEY";
    	public static final String KAPTCHA_SESSION_DATE = "KAPTCHA_SESSION_DATE";
    	public static final String KAPTCHA_SESSION_CONFIG_KEY = "kaptcha.session.key";
    	public static final String KAPTCHA_SESSION_CONFIG_DATE = "kaptcha.session.date";
    	public static final String KAPTCHA_BORDER = "kaptcha.border";
    	public static final String KAPTCHA_BORDER_COLOR = "kaptcha.border.color";
    	public static final String KAPTCHA_BORDER_THICKNESS = "kaptcha.border.thickness";
    	public static final String KAPTCHA_NOISE_COLOR = "kaptcha.noise.color";
    	public static final String KAPTCHA_NOISE_IMPL = "kaptcha.noise.impl";
    	public static final String KAPTCHA_OBSCURIFICATOR_IMPL = "kaptcha.obscurificator.impl";
    	public static final String KAPTCHA_PRODUCER_IMPL = "kaptcha.producer.impl";
    	public static final String KAPTCHA_TEXTPRODUCER_IMPL = "kaptcha.textproducer.impl";
    	public static final String KAPTCHA_TEXTPRODUCER_CHAR_STRING = "kaptcha.textproducer.char.string";
    	public static final String KAPTCHA_TEXTPRODUCER_CHAR_LENGTH = "kaptcha.textproducer.char.length";
    	public static final String KAPTCHA_TEXTPRODUCER_FONT_NAMES = "kaptcha.textproducer.font.names";
    	public static final String KAPTCHA_TEXTPRODUCER_FONT_COLOR = "kaptcha.textproducer.font.color";
    	public static final String KAPTCHA_TEXTPRODUCER_FONT_SIZE = "kaptcha.textproducer.font.size";
    	public static final String KAPTCHA_TEXTPRODUCER_CHAR_SPACE = "kaptcha.textproducer.char.space";
    	public static final String KAPTCHA_WORDRENDERER_IMPL = "kaptcha.word.impl";
    	public static final String KAPTCHA_BACKGROUND_IMPL = "kaptcha.background.impl";
    	public static final String KAPTCHA_BACKGROUND_CLR_FROM = "kaptcha.background.clear.from";
    	public static final String KAPTCHA_BACKGROUND_CLR_TO = "kaptcha.background.clear.to";
    	public static final String KAPTCHA_IMAGE_WIDTH = "kaptcha.image.width";
    	public static final String KAPTCHA_IMAGE_HEIGHT = "kaptcha.image.height";
    }
    
    

    可以使用上面的配置参数的名字在web.xml中配置
    配置和默认值说明可以参考下面的文章
    https://blog.csdn.net/elephantboy/article/details/52795309

    jsp demo修改

    <body>
    	<form action="/login" method="POST">
    		验证码:<input type="text" name="checkcode" /><img id="img" onclick="changeImg()"
    			src="${pageContext.request.contextPath}/KaptchaServlet"><br> <input
    			type="submit" value="提交" />
    
    	</form>
    </body>
    
    <script type="text/javascript">
     function changeImg(){
    	 var img =document.getElementById("img");
    	 //相同的请求,浏览器会将图片缓存下来,需要加一个参数,让每次请求都不一样,每次得到新的图片
    	 img.src="${pageContext.request.contextPath}/KaptchaServlet?time="+new Date().getTime();
     }
    </script>
    

    唯一的区别就是修改为KaptchaServlet

    提交表单时的验证码校验的区别如下,剩下没有任何区别

    		// 完成验证码的校验:
    		// 获取session按照它给的session key 获取
    		String code1 = (String)request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
    		// 接收前台表单提交的验证码的信息
    		String code2 = request.getParameter("checkCode");
    		if(code2==null || !code2.equalsIgnoreCase(code1)){
    			request.setAttribute("msg", "验证码输入不正确!");
    			request.getRequestDispatcher("/login.jsp").forward(request, response);
    			return;
    		}
    		
    

    实现的效果如下:
    在这里插入图片描述

    展开全文
  • 如果你上过网,那么你一定知道验证码。或许你为每一次登陆都要输入验证码而烦恼,或许你已经习惯了这种方式,或许,你还不知道,小小的验证码正有着越来越大的舞台! 写在前面的话 验证码,【Completely ...
  • 图片验证码

    2017-03-21 19:57:44
    图片验证码
  • 大家知道简单数字或者字母验证码很容易被破解,但是算式验证码或者中文汉字验证码不容易被破解,所以建议大家在使用验证码的时候,尽量用算式验证码或者中文汉字验证码。 下面是我写的两种验证码代码,有用到的朋友...
  • 验证码识别

    2019-04-14 22:53:45
    验证码识别 经过之前的几篇博文的铺垫,这一次我将会做一个比较麻烦的事情,就是验证码的识别。 在文章开始之前先附上上一次缺少的资源链接: 手写数字识别 是一个能够进行手写数字识别的简单的三层神经网络的代码和...
  • 对于爬取数据而言, 有的网站在登录时或者采集数据过程中,都会出现验证码。对于网络爬虫而言,解决验证码识别识别是非常重要的一件事。 今天,我们将讨论有关验证码的5件事,以帮助大家更好的进行网络数据抓取。 ...
  • 验证码破解

    2015-08-08 00:54:45
    所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片,图片里...学习验证码的破解/识别技术,不仅可以知道验证码的原理,而且可以让你知道怎样才能防止验证码被破解。 取出字模 识别验证码,
  • 验证码预处理

    2020-07-05 10:01:13
    前言 今天看到了一个好东西,和...具体就是对验证码做预处理,让我觉得是好东西的是验证码的切割部分。验证码样本: 这种验证码使用一些简单的技巧是无法切割的,而这个大佬用OpenCV做到了,并且切割效果比较理想。 k
  • 怎样避免短信验证码发送失败

    千次阅读 2018-12-06 17:49:01
    大家都知道生活中短信验证码是必不可少的,我们更改密码,确认身份还有日常交易,或者是使用app短信平台,都会接触到短信验证码,但是有时候会遇到点击“发送验证码”后没收到短信验证码的现象,这样对企业的发展...
  • 大家知道简单数字或者字母验证码很容易被破解,但是算式验证码或者中文汉字验证码不容易被破解, 所以建议大家在使用验证码的时候,尽量用算式验证码或者中文汉字验证码。 下面是我写的两种验证码代码,有用到的朋友...
  • 如果验证码是手机验证码请求,向页面传验证码,又要把验证码通过AJAX把值请求道聚合数据,这样一来就有两个AJAX,我不知道怎么处理。当然聚合数据短信验证不支持跨域访问,但是如果可以,怎么处理两个AJAX的运行和...
  • php实实现现汉汉字字验验证证...本文实例讲述了php实现汉字验证码和算式验证码的方法分享给大家供大家参考具体分析如下 大家知道简单数字或者字母验证码很容易被破解但是算式验证码或者中文汉字验证码不容易被破解 所以
  • BMP验证码

    2007-10-02 12:26:39
    无组件验证码,一直研究采用BMP格式做中文验证码,可不知道中文的BMP点阵列怎么获得.希望大虾们赐教!
  • 验证码算法

    千次阅读 2019-03-17 21:51:25
    首先要给出一个框,知道高和宽。 随机数:这个需要生成一个随机数,一个方法随机生成一种颜色,同时提供给X(宽),Y(高)不断变化; 字体的颜色:红、绿、蓝按照不同的比例组合成的,颜色的值是在(0-255之间) ...
  • 验证码教程

    千次阅读 2017-09-22 11:50:57
    我才知道有这样一个开源的触式验证码。touclick插件支持的功能有很多,比如:触式验证码、滑动验证码、拖拽验证码、点击验证码等。本文将详细的介绍touclick验证码如何使用。 touclick 详细介绍 touclick是国内首创...
  • 验证码全套代码,图片样式参考:...jar包里包含JAVA代码,web.xml和JSP页面代码,功能包括自定义图片尺寸和验证码长度,每个字符随机偏移角度,字符平滑边缘,含干扰线、噪点和背景扭曲。
  • 汉字验证码和算式验证码

    千次阅读 2015-04-08 09:29:19
    大家知道简单数字或者字母验证码很容易被破解,但是算式验证码或者中文汉字验证码不容易被破解,   所以建议大家在使用验证码的时候,尽量用算式验证码或者中文汉字验证码。    下面是我写的两种验证码代码,...
  • 验证码处理

    2019-03-27 16:22:00
    1、图片验证码 a. 比较简单的图片验证码    上面两个不用处理直接可以用OCR识别技术(利用python第三方库--tesserocr)来识别    背景比较糊    清晰可见 经过灰度变换和二值化后,由模糊的验证码背景...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,109
精华内容 16,043
关键字:

怎样知道验证码