cyycoish 发表于 2015-5-19 17:44:52

【asp.net】asp.net网页验证码生成之“奇葩”方式

网上有不少asp.net cs生成验证码的文档
试验了一下,很少能正常工作
于是我想出了一种生成验证码与表单验证分离的架构
好处是因为验证码生成程序分离,可以随时改进验证码生成算法
而不用修改网页代码
坏处是,调用了外部exe增加了服务器的负载
如果大量客户同时登陆,那么服务器和系统将不能正确工作。

先说一下想法:
生成验证码时,用CS调用VB写的EXE文件
VB的EXE在网站服务器目录下生成图片:
pic.bmp和图片答案ans.txt
CS在调用EXE后获取网站目录下图片然后读取答案
在提交表单时将读到的答案与用户填写的字符串进行比对。

关于vb验证码的生成算法,我在这篇帖子中略有叙述
主要算法大家可以参考这里:
【VB】最简单之验证码生成算法
http://www.0xaa55.com/thread-1096-1-1.html
(出处: 技术宅的结界)

这个新的验证码生成程序是在上述程序上改进而成
大致流程:在程序被调用时监测 是否后台运行参数
检测到以后生成验证码到Form的picturebox中
最后将答案输出至文本文件ans.txt,验证码图片用savepicture保
存至当前目录,以供调用。
那么在此贴上这次用的vb程序代码:

VERSION 5.00
Begin VB.Form Form1
   Caption         =   "Form1"
   ClientHeight    =   2940
   ClientLeft      =   60
   ClientTop       =   600
   ClientWidth   =   4680
   LinkTopic       =   "Form1"
   ScaleHeight   =   2940
   ScaleWidth      =   4680
   StartUpPosition =   3'窗口缺省
   Begin VB.CommandButton Command2
      Caption         =   "OK"
      Enabled         =   0   'False
      Height          =   255
      Left            =   2160
      TabIndex      =   3
      Top             =   480
      Width         =   975
   End
   Begin VB.TextBox Text1
      Height          =   285
      Left            =   -120
      TabIndex      =   2
      Top             =   480
      Width         =   2295
   End
   Begin VB.CommandButton Command1
      Caption         =   "Change"
      Height          =   495
      Left            =   2160
      TabIndex      =   1
      Top             =   0
      Width         =   975
   End
   Begin VB.PictureBox P
      AutoRedraw      =   -1'True
      BackColor       =   &H00FFFFFF&
      ForeColor       =   &H00000000&
      Height          =   495
      Left            =   0
      ScaleHeight   =   435
      ScaleWidth      =   2115
      TabIndex      =   0
      Top             =   0
      Width         =   2175
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit '从这里算真正开始

Dim ans         As Single
Dim bShutdown As Boolean

Private Sub Command1_Click()
    Randomize '初始化随机数生成器
    Command2.Enabled = True '免得不输就pass
    Dim a As Integer, b As Integer, c(3) As String, d As String
    'a放第一个数,b放第二个数,c放操作符,d保存操作符
    Dim i As Integer '计数器变量留着待用
   
    c(1) = "+"
    c(2) = "-"
    c(3) = "*"
   
    a = Int(Rnd * 10 + 1) '第一个随机数
    b = Int(Rnd * 10 + 1) '第二个随机数
   
   
    P.Cls '清屏
    '第一个数
    P.FontName = Screen.Fonts(Int(Rnd * 15) + 1) '字体名
    P.FontSize = Int(Rnd * 16) + 8 '字号
    P.FontBold = CBool(Int(Rnd + 1)) '是否粗体
    P.FontItalic = CBool(Int(Rnd + 1)) '是否斜体
    P.ForeColor = RGB(Int(Rnd * 254 + 1), Int(Rnd * 254 + 1), Int(Rnd * 254 + 1)) '随机颜色
    P.BackColor = RGB(Int(Rnd * 254 + 1), Int(Rnd * 254 + 1), Int(Rnd * 254 + 1))
    '定位
    P.CurrentX = Int(Rnd() * 10) + 10
    P.CurrentY = Int(Rnd() * 50) + 1
    P.Print CStr(a) '打印
   
    '操作符
    P.FontName = Screen.Fonts(Int(Rnd * 15) + 1)
    P.FontSize = Int(Rnd * 16) + 8
    P.FontBold = CBool(Int(Rnd + 1))
    P.FontItalic = CBool(Int(Rnd + 1))
    P.ForeColor = RGB(Int(Rnd * 254 + 1), Int(Rnd * 254 + 1), Int(Rnd * 254 + 1))
    P.CurrentX = Int(Rnd() * 500) + 500
    P.CurrentY = Int(Rnd() * 50) + 1
    d = c(Int(Rnd * 3) + 1) '随机操作符
    P.Print d
   
    '第二个数字,原理同上,解释略
    P.FontName = Screen.Fonts(Int(Rnd * 15) + 1)
    P.FontSize = Int(Rnd * 16) + 8
    P.FontBold = CBool(Int(Rnd + 1))
    P.FontItalic = CBool(Int(Rnd + 1))
    P.ForeColor = RGB(Int(Rnd * 254 + 1), Int(Rnd * 254 + 1), Int(Rnd * 254 + 1))
    P.CurrentX = Int(Rnd() * 1000) + 1000
    P.CurrentY = Int(Rnd() * 50) + 1
    P.Print CStr(b)
   
    '瞎画一些线条
    For i = 1 To 7
      P.ForeColor = RGB(Int(Rnd * 254 + 1), Int(Rnd * 254 + 1), Int(Rnd * 254 + 1))
      P.Line (Rnd * P.ScaleWidth, Rnd * P.ScaleWidth)-(Rnd * P.ScaleWidth, Rnd * P.ScaleWidth)
    Next
    '瞎画一些点
    For i = 1 To 100
      P.ForeColor = RGB(Int(Rnd * 254 + 1), Int(Rnd * 254 + 1), Int(Rnd * 254 + 1))
      P.PSet (Rnd * P.ScaleWidth, Rnd * P.ScaleWidth)
    Next
   
    '判定运算,计算结果
    Select Case d
    Case "+"
      ans = a + b
    Case "-"
      ans = a - b
    Case "*"
      ans = a * b
    End Select
   
    If bShutdown Then
      Dim fso As New FileSystemObject
      Dim f As TextStream
      Set f = fso.OpenTextFile(App.Path & "\ans.txt",

ForWriting, True)
      f.Write CStr(ans)
      f.Close
      SavePicture P.Image, App.Path & "\pic.bmp"
      End
    End If
End Sub

Private Sub Command2_Click()
    If Val(Text1) = ans And Text1 <> "" Then
      MsgBox "Pass!", 48
    Else
      Call Command1_Click
      Text1 = ""
    End If
End Sub

Private Sub Form_Load()
    If InStr(LCase(Command$), "-backstage") <> 0 Then
      Me.Visible = False
      bShutdown = True
    End If
    Call Command1_Click
End Sub



以下是网页cs实现的细节:
在网页中验证码图片用ImageButton存贮
使用ImageButton的ImageUrl属性给图片
那么使用Click事件则可以进行图片的改换
后来发现ImageButton的图片不能刷新
使用Response.AddHeader("Refresh", "0"); 刷新页面
图片即可正常显示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
//操作数据库需要的声明
using System.Data;
using System.Configuration;
using System.Collections;
using System.Data.SqlClient;
//调用外部EXE需要的声明
using System.Diagnostics;
using System.IO;

public partial class Default2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
      ImageButton1.ImageUrl = "pic.bmp";//在初始化页面时引入图片   
    }
    protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
    {
      GenerateACodeMap();//切换图片时重新加载图片
      Response.AddHeader("Refresh", "0"); //刷新页面
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
      string conStr = ConfigurationManager.ConnectionStrings["ConnectionStringMain"].ToString();
      SqlConnection myConn = new SqlConnection(conStr);
      myConn.Open();

      string queueStr = "SELECT * FROM AdminsTable WHERE Username = '" + TextBox1.Text + "' AND Password = '" + TextBox2.Text + "'";
      SqlCommand myCmd = new SqlCommand(queueStr, myConn);
      SqlDataReader myRdr = myCmd.ExecuteReader();
      //以上为连接数据库并查询
      string currentpath = System.AppDomain.CurrentDomain.BaseDirectory;//获取当前地址
      StreamReader sr = new StreamReader(currentpath + "ans.txt");
      String vcodeStr = sr.ReadLine();//获取服务器上存储的答案文件
      sr.Close();//关闭对文件的访问

      if (TextBox3.Text == vcodeStr.Replace("\n", ""))//判断验证码
      {
            if (myRdr.Read())//如果读到正确用户名与密码
            {
                Response.Write("<script>window.alert('hello!');</script>");//登陆成功
            }
            else
            {
                Response.Write("<script>window.alert('Bad Username Or Password!');</script>");//用户名密码错误
                TextBox1.Text = ""; TextBox2.Text = ""; TextBox3.Text = "";
            }
      }
      else
      {
            Response.Write("<script>window.alert('Bad Verifying Code!');</script>");//验证码错误
      }
      GenerateACodeMap();//刷新验证码
    }
    void GenerateACodeMap()//生成验证码
    {
      System.Diagnostics.ProcessStartInfo p = null;
      System.Diagnostics.Process Proc;

      string currentpath = System.AppDomain.CurrentDomain.BaseDirectory;
      p = new ProcessStartInfo("vcg.exe", "-backstage");//调用EXE给参“后台”
      p.WorkingDirectory = currentpath;
      p.WindowStyle = ProcessWindowStyle.Normal;
      
      Proc = System.Diagnostics.Process.Start(p);

      while (Proc.HasExited == false)//等待直到进程退出
      {
            if (Proc.HasExited == true)
            {
                ImageButton1.ImageUrl = "pic.bmp";//获取图片
            }
            else
            {
                ImageButton1.ImageUrl = "default.bmp";//获取不到图片 显示缺省图片
            }
      }
    }
}



具体实现:
用vs创建一只asp.net网站,使用cs语言。
建立一个如图页面:


将上述cs代码写入

将vb生成的exe命名为VCG放置在网站根目录下。



这样整个系统便完成了。

cyycoish 发表于 2015-5-19 17:49:28

哦 天哪 我的代码你肿么了!{:soso_e126:}

0xAA55 发表于 2015-5-19 22:29:21

cyycoish 发表于 2015-5-19 17:49
哦 天哪 我的代码你肿么了!

要学会不断编辑调整帖子……
页: [1]
查看完整版本: 【asp.net】asp.net网页验证码生成之“奇葩”方式