[fix]Config创建默认配置文件的开关Runtime.CreateConfigOnMissing,仅需对自动创建生效,而不应该阻止用户主动Save
智能大石头 authored at 2024-08-09 00:30:41 石头 committed at 2024-08-10 14:22:24
8.03 KiB
X
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
using System.Web.SessionState;
using NewLife.Collections;
using NewLife.Configuration;
using NewLife.Log;

namespace XControl
{
    /// <summary>
    /// 用于产生效验码图片自身,需要在web.config中配置为一个httpHandler,参考VerifyCodeBox类的备注
    /// </summary>
    public class VerifyCodeImageHttpHandler : IHttpHandler, IRequiresSessionState
    {
        private static string _SessionPrefix;
        /// <summary>
        /// 效验码的正确值在Session中存储的前缀
        /// </summary>
        private static string SessionPrefix
        {
            get
            {
                if (_SessionPrefix == null)
                {
                    _SessionPrefix = Config.GetConfig<string>("XControl.VerifyCode.SessionPrefix", typeof(VerifyCodeImageHttpHandler).FullName);
                }
                return _SessionPrefix;
            }
        }

        private static Random rand;

        /// <summary>构造方法</summary>
        public VerifyCodeImageHttpHandler()
        {
            if (rand == null)
            {
                rand = new Random();
            }
        }

        /// <summary>可重用</summary>
        public bool IsReusable
        {
            get { return true; }
        }

        /// <summary>处理请求</summary>
        /// <param name="context"></param>
        public void ProcessRequest(HttpContext context)
        {
            HttpRequest Request = context.Request;
            HttpResponse Response = context.Response;
            string verify = Request["verify"];
            string verifyCode = null;
            Response.ContentType = "image/Gif";
            Response.Cache.SetCacheability(HttpCacheability.NoCache);
            Response.Cache.SetNoStore();
            Response.Cache.SetMaxAge(TimeSpan.FromMilliseconds(0));
            Response.Cache.SetExpires(DateTime.MinValue);

            try
            {
                if (string.IsNullOrEmpty(verify))
                {
                    CreateErrorImage("错误的验证标识", Response.OutputStream);
                    return;
                }

                string[] verifycodes = new string[5];
                for (int i = 0; i < verifycodes.Length; i++)
                {
                    verifycodes[i] = rand.Next(10).ToString();
                }
                verifyCode = string.Join("", verifycodes);

                CreateVerifyCodeImage(verifyCode, Response.OutputStream);

                if (!string.IsNullOrEmpty(verifyCode))
                {
                    context.Session[SessionPrefix + "_" + verify] = verifyCode.ToLower();
                }
            }
            catch (Exception ex)
            {
                XTrace.WriteLine("生成验证码时发生了异常\r\n{0}", ex);
            }
        }

        /// <summary>效验指定输入是否和指定请求产生的验证码相同</summary>
        /// <param name="input">用户输入</param>
        /// <param name="verify">验证标识,一般是VerifyCodeBox.VerifyGUID</param>
        /// <param name="context"></param>
        /// <returns></returns>
        public static bool VerifyCode(string input, string verify, HttpContext context)
        {
            if (string.IsNullOrEmpty(input)) return false;
            return input.ToLower().Equals(context.Session[SessionPrefix + "_" + verify]);
        }

        /// <summary>复位指定验证标识,除非再次请求验证码,否则任何VerifyCode方法的调用都会返回false</summary>
        /// <param name="verify"></param>
        /// <param name="context"></param>
        public static void ResetVerifyCode(string verify, HttpContext context)
        {
            if (string.IsNullOrEmpty(verify)) return;
            context.Session.Remove(SessionPrefix + "_" + verify);
        }

        private static Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
        private static string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial", "宋体" };

        /// <summary>
        /// 创建指定验证码的图片,将结果输出到指定流中
        /// </summary>
        /// <param name="verifyCode"></param>
        /// <param name="stream"></param>
        private void CreateVerifyCodeImage(string verifyCode, Stream stream)
        {
            using (Bitmap image = new Bitmap((int)Math.Ceiling(verifyCode.Length * 28.5), 30))
            {
                using (Graphics g = Graphics.FromImage(image))
                {
                    // 清空图片
                    g.Clear(Color.White);
                    // 背景噪音线
                    for (int i = 0; i < 20; i++)
                    {
                        int x1 = rand.Next(image.Width);
                        int x2 = rand.Next(image.Width);
                        int y1 = rand.Next(image.Height);
                        int y2 = rand.Next(image.Height);
                        g.DrawLine(new Pen(colors[rand.Next(colors.Length)]), x1, y1, x2, y2);
                    }

                    for (int i = 0; i < verifyCode.Length; i++)
                    {
                        int cindex = rand.Next(7);
                        int findex = rand.Next(5);

                        Font drawfont = new Font(fonts[rand.Next(fonts.Length)], 18, FontStyle.Bold);
                        SolidBrush drawbrush = new SolidBrush(colors[rand.Next(colors.Length)]);

                        RectangleF drawrect = new RectangleF(
                            5.0F + rand.Next(10) + i * 25,
                            .0F + rand.Next(image.Height - 25),
                            20.0F,
                            25.0F
                            );

                        StringFormat drawformat = new StringFormat();
                        drawformat.Alignment = StringAlignment.Center;

                        g.DrawString(verifyCode[i].ToString(), drawfont, drawbrush, drawrect, drawformat);
                    }

                    for (int i = 0; i < 100; i++)
                    {
                        int x = rand.Next(image.Width);
                        int y = rand.Next(image.Height);
                        image.SetPixel(x, y, Color.FromArgb(rand.Next()));
                    }

                    g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);

                    image.Save(stream, ImageFormat.Gif);
                }
            }
        }

        private SolidBrush ErrorBrush;
        private Font ErrorFont;
        DictionaryCache<string, byte[]> Messages = new DictionaryCache<string, byte[]>();

        /// <summary>创建错误图片,不要使用这个方法显示动态错误消息</summary>
        /// <param name="msg"></param>
        /// <param name="stream"></param>
        private void CreateErrorImage(string msg, Stream stream)
        {
            var ret = Messages.GetItem(msg, delegate(string key)
            {
                if (ErrorFont == null)
                {
                    ErrorFont = new Font(FontFamily.GenericSansSerif, 16);
                    ErrorBrush = new SolidBrush(Color.Black);
                }
                using (Bitmap image = new Bitmap(msg.Length * 57, 30))
                {
                    using (Graphics g = Graphics.FromImage(image))
                    {
                        g.Clear(Color.White);
                        g.DrawString(msg, ErrorFont, ErrorBrush, 0, 0);

                        MemoryStream ms = new MemoryStream();
                        image.Save(ms, ImageFormat.Gif);
                        return ms.ToArray();
                    }
                }
            });
            if (ret != null)
            {
                stream.Write(ret, 0, ret.Length);
            }
        }
    }
}