业务部门提出要做一个客户自助查询的系统,而且这个系统是要公布到互联网上的。这样的话,除了一般常见的防注入、服务器安全外,最简单的就是在用户登录处加一个验证码,可以在一定程度上加大恶意尝试破解用户密码的难度。
在网上搜索了一天的类似内容,做成了一个自己的小程序,与大家共同分享,并将使用过程中的小细节说一下,一则备忘,二则也许能让后来的朋友多了解到一点东西吧。
效果图:
老样子,先上代码。个人的习惯,对于做为系统的纯输出功能的代码,使用ashx文件(一般处理程序)而不是aspx文件,呵呵。
先建立一个verify_code.ashx文件,代码如下:
验证码图片生成程序
<%@ WebHandler Language="VB" Class="verify_code" %>Imports SystemImports System.DrawingImports System.Drawing.Drawing2DImports System.Drawing.ImagingImports System.Drawing.TextImports System.WebImports System.Web.SessionStateImports System.Web.ConfigurationPublic Class verify_code : Implements IHttpHandler, IRequiresSessionState Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest '定义图片大小 Dim bitMapImage As New Bitmap(80, 24) Dim verity_code As String '生成图片 DisturbBitmap(bitMapImage) Dim graphicImage As Graphics = Graphics.FromImage(bitMapImage) graphicImage.SmoothingMode = SmoothingMode.HighSpeed '指定字体、大小、风格 Dim fontfamily As New FontFamily("Consolas") Dim font As New Font(fontfamily, 20, FontStyle.Regular, GraphicsUnit.Pixel) '生成六位随机字符,使用GetRandomint为生成一个六位随机数字 verity_code = GetRanChar(6) '将随机字符写到图片中 graphicImage.DrawString(verity_code, font, Brushes.Green, New Point(1, 1)) '在外围画一个边框 graphicImage.DrawRectangle(New Pen(Color.Green, 0), 0, 0, bitMapImage.Width - 1, bitMapImage.Height - 1) '输出格式为JPG文件 context.Response.ContentType = "image/jpeg" '将生成的验证码写到Session中,供程序判断 context.Session("verify_code") = verity_code '输出验证码图片 bitMapImage.Save(context.Response.OutputStream, ImageFormat.Jpeg) graphicImage.Dispose() bitMapImage.Dispose() End Sub Public Function GetRandomint() As String Dim random As New Random() Return (random.[Next](100000, 999999).ToString()) End Function Public Function GetRanChar(Optional ByVal vinum As Integer = 6) As String Dim Vchar As String Dim Vnum As String = "" If vinum = 0 Then vinum = 6 End If '字符串中没有使用0和O,以及小写的L,以免在验证码上看不清楚 Vchar = "2,A,B,C,D,1,E,F,G,H,I,4,J,K,L,M,N,6,P,Q,R,S,T,8,U,W,X,Y,7,Z,a,b,c,d,e,5,f,g,h,i,j,k,m,n,p,3,q,r,s,t,9,u,v,w,x,y,z" Dim VcArray() As String = Vchar.Split(",") Dim random As New Random() Dim i As Integer Dim iNum As Integer For i = 1 To vinum iNum = VcArray.Length While iNum = VcArray.Length iNum = Convert.ToInt32((VcArray.Length) * random.NextDouble()) End While Vnum = Vnum + VcArray(iNum) Next Return Vnum End Function Private Sub DisturbBitmap(ByVal map As Bitmap) Dim random As New Random() '通过随机数生成 Dim k As Integer = 0 While k < 80 Dim j As Integer = 0 While j < 24 '在8%的随机位置产生噪点,100就是无噪点,一般不要小于85 If random.Next(0, 100) <= 92 Then map.SetPixel(k, j, Color.AliceBlue) End If System.Math.Max(System.Threading.Interlocked.Increment(j), j - 1) End While System.Math.Max(System.Threading.Interlocked.Increment(k), k - 1) End While End Sub Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable Get Return False End Get End PropertyEnd Class
使用以下方式就可以在页面指定的地方显示验证码:
<img id="Verify_code" src="Verify_Code.ashx" width="80" height="24" />
代码应该都不复杂,大家可以看明白,如果不明白,复制过去也能使用,就不再多说了,因为这方面的资料也很多,下面就我在编写过程中发现的一些小细节与大家共同讨论一下:
一、验证码的刷新:
如果生成的验证码实在看不清楚,用户就需要刷新一个验证码,很多网站都提供了点击验证码后自动刷新,查看了一下代码,有些是使用了jquery的ajax功能,但我感觉有些复杂,后来用下面这种方法处理了,效果也极好。
<img id="Verify_code" src="Verify_Code.ashx?" alt="看不清?点击更换" onclick="this.src=this.src+'?'" width="80" height="24" />
二、验证码使用的字体:
如果使用默认的字体,网上的很多识别器都能很快的识别,因为我就想到了用一些不常见的字体来处理这个问题,但在使用中发现了以下的几个情况,大家以后也可用来参考:
1、并不是每一种在字体文件夹中的字体都可以使用,要asp.net能识别出来的才行,否则就只能使用默认字体;
2、新安装的字体有时不能正常使用,网上有资料说是重启一下IIS,但我是直接重启了服务器,有部分字体就被识别出来了;
3、建议使用等宽字体,如果是不等宽字体,万一生成的验证码都是W、M这些宽体字符的话,可能原来指定的图片放不下,影响效果,个人建议使用Consolas字体;
4、使用不同的字体时,字体大小会有很大差别,需要很多次尝试后使用最合适的字体大小,太窄的字体、笔划太细的字体都不适合做为验证码,加上噪点后很看识别出来,也可以使用手写体的数字(最好不要用字母了,不是外国人,真的不好认),不加噪点,也是一种方法。
下面为大家提供一个代码,可以枚举出系统中已安装且可以被asp.net识别的字体,这些字体就可以用在验证码的生成上了:
原文:http://msdn.microsoft.com/zh-cn/library/0yf5t4e8(v=VS.80).aspx
新建一个test.aspx文件,将代码放到test.aspx.vb中直接运行就可以了,前台代码不用修改:
枚举已安装的字体Imports System.DrawingImports System.Drawing.TextPartial Class test Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim fontFamily As New FontFamily("Arial") Dim familyName As String Dim familyList As String = "" Dim fontFamilies() As FontFamily Dim installedFontCollection As New InstalledFontCollection() fontFamilies = installedFontCollection.Families Dim count As Integer = fontFamilies.Length Dim j As Integer While j < count familyName = fontFamilies(j).Name familyList = familyList & familyName familyList = familyList & ", " j += 1 End While Response.Write(familyList) End SubEnd Class
大家可以根据自己页面的配色方案,为验证码图片设定不同的背景色、字体颜色、边框色等,我在尝试时发现,颜色那里只接受color数据类型,而不是常见的HTML颜色代码,我也没有再去研究怎么将颜色转换过去了,将就点用吧,但它的颜色都是用单词来设定了,我找到了一个指定颜色的网页,大家可以用来进行一个参考:
颜色代码对照表:http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/colors/ColorTable.htm
测试时发现一个问题,在VS里F5时,没有错,在客户端进行测试时,会因为浏览器缓存的原因导致显示的图片与实际的不符,但这种情况不多且一般客户不会像测试时一样大量的刷新,我就没有再处理,有需要可以自己加上不缓存的代码。
这个验证码系统使用的是用Session来存储验证码的,因此就会有一个情况,如用户打开了二个及以上的窗口时,可能提交到后台时,后台就只会判断最后一次生成的验证码了,如果你的系统要求避免这种情况的话,可以先将生成的验证码进行加密,把加密字符串赋值给一个hidden控件,不过这样的话可能使用jquery控件来处理就更方便了,有空我也试一下,呵呵。
能想到的基本就是这么多吧,大家可以对代码自己进行修改,加上斜体、黑体、删除线、下划线等随机效果,加强验证码的强度。
没有使用更复杂的效果,是考虑到验证码只是一种手段,我们需要其他更多的后台手段来实现系统的安全,如果把精力都放在了验证码上,生怕被识别系统识别出来,一个验证码做的要用户刷新七八次才看认出一部分的话,那就应该本末倒置了,不是提供系统安全,而是折腾人了,这样的话,如果不是非的必要,是没有人愿意再来访问你的网站了。
主要参考资料:
http://www.cnblogs.com/thcjp/archive/2006/07/06/444342.html
来源:https://www.cnblogs.com/vvian/archive/2010/12/25/1916792.html