| 
UID418精华积分4005威望 点宅币 个贡献 次宅之契约 份最后登录1970-1-1在线时间 小时 
 | 
 
| 网上有不少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放置在网站根目录下。
 
   
 
 这样整个系统便完成了。
 
 | 
 |