技术开发 频道

开发基于ASP.NET WebService的图片验证码服务



【IT168 技术文档】

    最近,工作中接到一项任务,开发一个页面验证码功能,查阅了一些网上的资料,并结合以前的绘图方面的知识,实现了如下的解决方案。

  要解决的问题:

  1. 如何随机生成图片

    生成system.drawing.bitmap对象,使用system.drawing.graphics向位图对象中绘图。

  2. 如何在webservice的方法中通过参数传递图片数据

    将bitmap对象输出成字节流,webmothod使用字节数组返回该字节流。

  实例:

  1. 用vs.net 2003创建一个asp.net webservice工程,默认的service名为myservice,为myservice添加一个名为generateverifyimage的webmethod。该方法的代码如下:

/// <summary> /// 生成图片验证码 /// </summary> /// <param name="nlen">验证码的长度</param> /// <param name="strkey">输出参数,验证码的内容</param> /// <returns>图片字节流</returns> [webmethod] public byte[] generateverifyimage(int nlen,ref string strkey) { int nbmpwidth = 13*nlen+5; int nbmpheight = 25; system.drawing.bitmap bmp = new system.drawing.bitmap(nbmpwidth,nbmpheight); // 1. 生成随机背景颜色 int nred,ngreen,nblue; // 背景的三元色 system.random rd = new random((int)system.datetime.now.ticks); nred = rd.next(255)%128+128; ngreen = rd.next(255)%128+128; nblue = rd.next(255)%128+128; // 2. 填充位图背景 system.drawing.graphics graph = system.drawing.graphics.fromimage(bmp); graph.fillrectangle(new solidbrush(system.drawing.color.fromargb(nred,ngreen,nblue)) ,0 ,0 ,nbmpwidth ,nbmpheight); // 3. 绘制干扰线条,采用比背景略深一些的颜色 int nlines = 3; system.drawing.pen pen = new system.drawing.pen(system.drawing.color.fromargb(nred-17,ngreen-17,nblue-17),2); for(int a =0;a< nlines;a++) { int x1 = rd.next() % nbmpwidth; int y1 = rd.next() % nbmpheight; int x2 = rd.next() % nbmpwidth; int y2 = rd.next() % nbmpheight; graph.drawline(pen,x1,y1,x2,y2); } // 采用的字符集,可以随即拓展,并可以控制字符出现的几率 string strcode = "abcdefghijklmnopqrstuvwxyz"; // 4. 循环取得字符,并绘制 string strresult = ""; for(int i=0;i<nlen;i++) { int x = (i*13 + rd.next(3)); int y = rd.next(4) + 1; // 确定字体 system.drawing.font font = new system.drawing.font("courier new", 12 + rd.next()%4, system.drawing.fontstyle.bold); char c = strcode[rd.next(strcode.length)]; // 随机获取字符 strresult += c.tostring(); // 绘制字符 graph.drawstring(c.tostring(), font, new solidbrush(system.drawing.color.fromargb(nred-60+y*3,ngreen-60+y*3,nblue-40+y*3)), x, y); } // 5. 输出字节流 system.io.memorystream bstream = new system.io.memorystream(); bmp.save(bstream,system.drawing.imaging.imageformat.jpeg); bmp.dispose(); graph.dispose(); strkey = strresult; byte[] bytereturn = bstream.toarray(); bstream.close(); return bytereturn; }

 
 2. 测试webmethod,添加一个webform,引用上述webservice,引用名为imagesvr。在page_load中添加代码:

   ...
   imagesvr.myservice imgsvr = new imagesvr.myservice();
   string strkey = "";
   byte[] data = imgsvr.generateverifyimage(5,ref strkey);
   response.outputstream.write(data,0,data.length);
   ...

  3. 运行。每次refresh这个webform时,就会显示一个新生成的图片验证码,而函数的输出参数strkey保存的就是这个验证码的实际内容,可以保存在session中,作为验证使用。

  上次开发出图片验证码之后,根据一些朋友的建议,本着验证码易识别(针对人),不易破解,美观的原则,改进了验证码生成的算法,采用图像滤镜的方法,对图片验证码进行反破解干扰,结果图片示例如下:

  滤镜效果主要采用波形(wave)算法,通过对x轴y轴的正弦波形处理,产生叠加效果。算法主要描述如下:

private const double pi = 3.1415926535897932384626433832795; private const double pi2 = 6.283185307179586476925286766559; /// <summary> /// 正弦曲线wave扭曲图片 /// </summary> /// <param name="srcbmp"></param> /// <param name="bxdir"></param> /// <param name="nmultvalue">波形的幅度倍数</param> /// <param name="dphase">波形的起始相位,取值区间[0-2*pi)</param> /// <returns></returns> public system.drawing.bitmap twistimage(bitmap srcbmp,bool bxdir,double dmultvalue,double dphase) { system.drawing.bitmap destbmp = new bitmap(srcbmp.width,srcbmp.height); // 将位图背景填充为白色 system.drawing.graphics graph = system.drawing.graphics.fromimage(destbmp); graph.fillrectangle(new solidbrush(system.drawing.color.white),0,0,destbmp.width,destbmp.height); graph.dispose(); double dbaseaxislen = bxdir ? (double)destbmp.height : (double)destbmp.width; for(int i=0;i<destbmp.width;i++) { for(int j=0;j<destbmp.height;j++) { double dx = 0; dx = bxdir ? (pi2*(double)j)/dbaseaxislen : (pi2*(double)i)/dbaseaxislen; dx += dphase; double dy = math.sin(dx); // 取得当前点的颜色 int noldx = 0,noldy = 0; noldx = bxdir ? i + (int)(dy*dmultvalue) : i; noldy = bxdir ? j : j + (int)(dy*dmultvalue); system.drawing.color color = srcbmp.getpixel(i,j); if(noldx >= 0 && noldx < destbmp.width && noldy >=0 && noldy < destbmp.height) { destbmp.setpixel(noldx,noldy,color); } } } return destbmp; }


  开头的示例图片,是两次波形效果的叠加,两次效果分别针对x轴方向和y轴方向,如果取消对边缘背景色的填充,可以看到算法对图形的影响,如下图:

  这样产生的验证码,看起来很像google站点上的验证码吧,当然,如果你有兴趣,还可以添加其他的滤镜效果,如拉伸,旋转,马赛克等。但是注意一点,网站验证码不是越复杂越好,要在速度和安全上找到一个平衡点。

0
相关文章