验证码上的高效正弦扭曲函数
作者:tupunco 日期:2010-01-30
c#上, 但凡需要给验证码添加扭曲的地方感觉都是一个函数:
#region 产生波形滤镜效果
private const double PI = 3.1415926535897932384626433832795;
private const double PI2 = 6.283185307179586476925286766559;
/// <summary>
/// 正弦曲线Wave扭曲图片
/// </summary>
/// <param name="srcBmp">图片路径</param>
/// <param name="bXDir">如果扭曲则选择为True</param>
/// <param name="nMultValue">波形的幅度倍数,越大扭曲的程度越高,一般为3</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;
}
#endregion
这个函数实在效率不高, 但还是很多人用. 可以从下面的地址得到结果:
Tags: 高效 扭曲函数 C# TwistImage 验证码
KCAPTCHA 验证码波纹扭曲函数 C# 实现
作者:tupunco 日期:2010-01-30
KCAPTCHA 一个不错的验证码实现, 官方网站:http://www.captcha.ru/en/kcaptcha/. 因为工作原因, 需要改进验证码所有有机会把波纹扭曲部分使用 C# 实现.
#region KCAPTCHA 波纹扭曲
/// <summary>
/// # KCAPTCHA PROJECT VERSION 1.2.6
/// www.captcha.ru, www.kruglov.ru
/// 波形扭曲 FROM KCAPTCHA
/// </summary>
/// <param name="srcBmp">待扭曲的图像 必须为 PixelFormat.Format24bppRgb 格式图像</param>
/// <returns></returns>
private static Bitmap WaveDistortion(Bitmap srcBmp)
{
if (srcBmp == null)
return null;
if (srcBmp.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("srcBmp PixelFormat.Format24bppRgb 格式图像", "srcBmp");
var width = srcBmp.Width;
var height = srcBmp.Height;
Bitmap destBmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
{
//前景色
Color foreground_color = Color.FromArgb(randx.Next(10, 100), randx.Next(10, 100), randx.Next(10, 100));
//背景色
Color background_color = Color.FromArgb(randx.Next(200, 250), randx.Next(200, 250), randx.Next(200, 250));
using (Graphics newG = Graphics.FromImage(destBmp))
{
newG.Clear(background_color);
// periods 时间
double rand1 = randx.Next(710000, 1200000) / 10000000.0;
double rand2 = randx.Next(710000, 1200000) / 10000000.0;
double rand3 = randx.Next(710000, 1200000) / 10000000.0;
double rand4 = randx.Next(710000, 1200000) / 10000000.0;
// phases 相位
double rand5 = randx.Next(0, 31415926) / 10000000.0;
double rand6 = randx.Next(0, 31415926) / 10000000.0;
double rand7 = randx.Next(0, 31415926) / 10000000.0;
double rand8 = randx.Next(0, 31415926) / 10000000.0;
// amplitudes 振幅
double rand9 = randx.Next(330, 420) / 110.0;
double rand10 = randx.Next(330, 450) / 110.0;
double amplitudesFactor = randx.Next(5, 6) / 10.0;//振幅小点防止出界
double center = width / 2.0;
//wave distortion 波纹扭曲
BitmapData destData = destBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, destBmp.PixelFormat);
BitmapData srcData = srcBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, srcBmp.PixelFormat);
for (var x = 0; x < width; x++)
{
for (var y = 0; y < height; y++)
{
var sx = x + (Math.Sin(x * rand1 + rand5)
+ Math.Sin(y * rand3 + rand6)) * rand9 - width / 2 + center + 1;
var sy = y + (Math.Sin(x * rand2 + rand7)
+ Math.Sin(y * rand4 + rand8)) * rand10 * amplitudesFactor; //振幅小点防止出界
int color, color_x, color_y, color_xy;
Color overColor = Color.Empty;
if (sx < 0 || sy < 0 || sx >= width - 1 || sy >= height - 1)
{
continue;
}
else
{
color = BitmapDataColorAt(srcData, (int)sx, (int)sy).B;
color_x = BitmapDataColorAt(srcData, (int)(sx + 1), (int)sy).B;
color_y = BitmapDataColorAt(srcData, (int)sx, (int)(sy + 1)).B;
color_xy = BitmapDataColorAt(srcData, (int)(sx + 1), (int)(sy + 1)).B;
}
if (color == 255 && color_x == 255 && color_y == 255 && color_xy == 255)
{
continue;
}
else if (color == 0 && color_x == 0 && color_y == 0 && color_xy == 0)
{
overColor = Color.FromArgb(foreground_color.R, foreground_color.G, foreground_color.B);
}
else
{
double frsx = sx - Math.Floor(sx);
double frsy = sy - Math.Floor(sy);
double frsx1 = 1 - frsx;
double frsy1 = 1 - frsy;
double newColor =
color * frsx1 * frsy1 +
color_x * frsx * frsy1 +
color_y * frsx1 * frsy +
color_xy * frsx * frsy;
if (newColor > 255) newColor = 255;
newColor = newColor / 255;
double newcolor0 = 1 - newColor;
int newred = Math.Min((int)(newcolor0 * foreground_color.R + newColor * background_color.R), 255);
int newgreen = Math.Min((int)(newcolor0 * foreground_color.G + newColor * background_color.G), 255);
int newblue = Math.Min((int)(newcolor0 * foreground_color.B + newColor * background_color.B), 255);
overColor = Color.FromArgb(newred, newgreen, newblue);
}
BitmapDataColorSet(destData, x, y, overColor);
}
}
destBmp.UnlockBits(destData);
srcBmp.UnlockBits(srcData);
}
if (srcBmp != null)
srcBmp.Dispose();
}
return destBmp;
}
/// <summary>
/// 获得 BitmapData 指定坐标的颜色信息
/// 实现 PHP imagecolorat
/// </summary>
/// <param name="srcData">从图像数据获得颜色 必须为 PixelFormat.Format24bppRgb 格式图像数据</param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>x,y 坐标的颜色数据</returns>
/// <remarks>
/// Format24BppRgb 已知X,Y坐标,像素第一个元素的位置为Scan0+(Y*Stride)+(X*3)。
/// 这是blue字节的位置,接下来的2个字节分别含有green、red数据。
/// </remarks>
static Color BitmapDataColorAt(BitmapData srcData, int x, int y)
{
if (srcData.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("srcData PixelFormat.Format24bppRgb 格式图像数据", "srcData");
byte[] rgbValues = new byte[3];
Marshal.Copy((IntPtr)((int)srcData.Scan0 + ((y * srcData.Stride) + (x * 3))), rgbValues, 0, 3);
return Color.FromArgb(rgbValues[2], rgbValues[1], rgbValues[0]);
}
/// <summary>
/// 设置 BitmapData 指定坐标的颜色信息
/// 实现 PHP ImageColorSet
/// </summary>
/// <param name="destData">设置图像数据的颜色 必须为 PixelFormat.Format24bppRgb 格式图像数据</param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">待设置颜色</param>
/// <remarks>
/// Format24BppRgb 已知X,Y坐标,像素第一个元素的位置为Scan0+(Y*Stride)+(X*3)。
/// 这是blue字节的位置,接下来的2个字节分别含有green、red数据。
/// </remarks>
static void BitmapDataColorSet(BitmapData destData, int x, int y, Color color)
{
if (destData.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("destData PixelFormat.Format24bppRgb 格式图像数据", "destData");
byte[] rgbValues = new byte[3] { color.B, color.G, color.R };
Marshal.Copy(rgbValues, 0, (IntPtr)((int)destData.Scan0 + ((y * destData.Stride) + (x * 3))), 3);
}
#endregion
另外一些验证码游优化有关的链接:
- http://www.captcha.ru/en/kcaptcha/ (KCAPTCHA 官方)
- http://zh.wikipedia.org/wiki/CAPTCHA
- http://en.wikipedia.org/wiki/CAPTCHA
- http://caca.zoy.org/wiki/PWNtcha
- http://www.brains-n-brawn.com/default.aspx?vDir=aicaptcha
- http://www.captcha.net/
还有一些验证码识别的链接:
编程中使用的数学(2):权重随机
作者:tupunco 日期:2010-01-19
博客也停了半年多了, 半年内好多事情, 时间和机会可能都不适合写下点东西, 2010年了, 开个好头. 之前的一篇写在2008年6月份, 这篇写在一年半以后, 真是个笑话.
步入正题. '权重随机'是去年九十月份工作中一个抽奖问题引出的. 需求需要达到不同奖品需要有不同的获得几率, 在查找资料中找到了以下几篇文章:
- http://zzk.cnblogs.com/s?w=%e6%9d%83%e9%87%8d+%e9%9a%8f%e6%9c%ba&p=1
- 生成安全的随机数 http://www.cnblogs.com/rainy/archive/2006/08/05/468670.html
- http://www.cnblogs.com/wdfrog/archive/2007/12/14/994963.html
按'权重随机'过程假设编程平台上的提供的随机发生器产生的每个结果都是等概率的(实际上.NET平台和其他编程平台上提供了很好的满足本条件的随机发生器), 另外一个条件是'权重'以非负整形数的形式提供. 假如'待随机项'集合为 {I,…,In}, 其对应的权重结合为 {W,…,Wn}, 在产生随机数的时候随机的范围为(0, ∑Wi), 其中∑Wi表示为'所有待随机项权重的和', 使用随机器产生的随机结果假如在 (wi, wi+1] 范围了(注意开闭区间), 则当前的随机结果项为Ii. 可以简单理解为以下的文字描述: 把所有'待随机项'的权重和作为要随机的上限, '随机项'前后排好序, 生成的随机结果如果在某两个个随机项(A,B)权重区间范围内, 那么随机的结果项就取A.
软件开发经典书籍的魅力
作者:tupunco 日期:2009-04-06
读软件开发方面书籍自己似乎有偏好,不喜欢看国内作者写的(不过有好书也是看的,比如博文视点的好多原创经典书籍)大部分看的是国外书籍的译本(英语不好只能看译本),译本也是有选择的,如果是一堆人翻译的基本也不看,除非这本书讲的方面确实就这么一本书,并且翻译的也不赖。博客园有一.NET方面的牛人名叫“老赵”,他的博客格言是“让老外看中国人写的计算机书籍”,甚是敬仰。
过年前后接手的项目是一个“客户端数据录入”的软件,之前开发的都是B/S软件,对WinForm开发中的某些要求也不甚了解,所以按着WEB软件的模式来建立“数据连接层”、“业务逻辑层”。其实自己使用了一个“SocanCode”的代码生成器来生成了“数据连接层”和“业务逻辑层”,由于客户要求的这个客户端软件除了要有一个“中心版”还要一个“部门版”的(软件为什么没有直接设计成B/S版的呢?因为客户的理由是“部门版”的用户可能没有网络,并且点名要搞两个版本),考虑到性能方面的要求所以“部门版”数据库用Access,“中心版”使用SQL Server 2000 个人版,所以这个“代码生成器”生成了两套“数据连接层”代码,真是不错的东西。
“SocanCode”生成的代码类似于“PetShop 4”,模式的代码,“数据实体模型”部分直接就是实体类,软件在实现的时候碰到一个问题:数据库的某些字段的值与其他字段有隐藏的“关联”关系,比如一个XX号=某些字典字段+某些字典字段+自编号。数据库在设计的时候实际上已经违背了某些“数据库设计范式”,因为为了最后呈现的时候方便,也为了客户在导出数据的时候方便。为了实现这个“关联”最后每个“数据实体模型”实现了“System.ComponentModel.INotifyPropertyChanged”接口,来“通知”某些列的更改,通过“PropertyChanged”事件来捕捉“通知”,再“Remove”事件,更改某些“关联”关系的字段值,再“Add”事件来完成整一个“关联”关系字段的控制。虽然实现起来麻烦了点,但客户再提“关联”关系字段更改的需求改的就很方便了。
ASP.NET MVC CRUD 实例
作者:tupunco 日期:2009-03-24
ASP.NET MVC 1.0 已经于上周发布了, RC1的时候写了两个例子, 一个是一个CRUD的例子, 一个是仿蓝色梦想作品集URL路径的例子. CRUD例子是一个拥有完整的从"数据源"查询数据列表/单条详细/删除/更新添加的功能的列子, 仿蓝色梦想作品集URL路径的例子是模仿作品集的URL切换方式, 展示了ASP.NET MVC 功能强大的路由功能, 自定义控件的列子. 本文事CRUD例子的说明和技术细节.
ASP.NET MVC安装/创建项目的方法可以参看http://www.asp.net/mvc的说明教程.
创建项目完毕后, 创建一个数据模型(实际为一数据实体)和数据服务(Fake的数据仓库)部分代码:
namespace MvcApplicationCRUD.Models
{
public class People
{
public int Id { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
}
[原创]C#中使用反射
作者:tupunco 日期:2009-03-09
过了年到现在忙了起来, 不像年前, 可以整月的闲着, 不过闲着也有好处, 学习了好多东西. 过了年的项目中多次用到了 .Net 反射机制, 使得其间巧妙地解决了很多问题. 总结一下使用到的地方.
1.使用工厂模式来抽象数据连接层之用(PetShop模式的三层结构内的使用方式).
1: System.Reflection.Assembly assembly = Assembly.LoadFile("Assembly Path");
2: IXXXXXDAL dal = (IXXXXXDAL)assembly.CreateInstance("Namespace.DAL.XXXXXDAL");
2.使用 "System.Activator.CreateInstance()" 方式来创建对象.
1: //
2: // 摘要:
3: // 创建类型的一个实例,该类型由指定的泛型类型参数指定。
4: //
5: // 类型参数:
6: // T:
7: // 要创建的类型。
8: //
9: // 返回结果:
10: // 对新创建对象的引用。
11: public static T CreateInstance<T>();
12: //
13: // 摘要:
14: // 使用指定类型的默认构造函数来创建该类型的实例。
15: //
16: // 参数:
17: // type:
18: // 要创建的对象的类型。
19: //
20: // 返回结果:
21: // 对新创建对象的引用。
22: public static object CreateInstance(Type type);
3.显示某对象的子级属性值.
1: public static string DisplayPropertyInfo(object obj)
2: {
3: StringBuilder sb = new StringBuilder();
4: Type type = obj.GetType();
5: sb.AppendFormat("[{0}]\r\n",obj);
6: foreach (var item in type.GetProperties())
7: {
8: sb.AppendFormat("\t[{0}:{1}]\r\n", item.Name,
9: item.GetValue(obj, null));
10: }
11: return sb.ToString();
12: }
4.转换一个DataTable类型的对象成为一个实体集合(Table的列与实体类的属性是一一对应的).
/// <summary>
/// 数据服务基类对象
/// </summary>
public abstract class DataServiceBase
{
#region DB 处理
/// <summary>
/// 执行SQL字符串返回一个DataTable
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
protected DataTable ExecuteSQL(string sql)
{
//*****返回一个DataTable****
return null;
}
#endregion
#region Tabel 类型转换相关
/// <summary>
/// 转化Table成一个相对应的实体类集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="table"></param>
/// <returns></returns>
protected List<T> ConvertDataTableToEntityCollections<T>(DataTable table)
where T : class, new()
{
if (table != null)
{
PropertyInfo[] pInfos = GetTypePropertyInfo(typeof(T));
List<T> list = new List<T>(table.Rows.Count);
foreach (DataRow row in table.Rows)
{
T t = new T();
foreach (var pInfo in pInfos)
{
if (table.Columns.Contains(pInfo.Name))
{
//HACK: 对象转换成指定的类型使用 Convert.ChangeType
// Convert.ChangeType(row[pInfo.Name], pInfo.PropertyType);
try
{
pInfo.SetValue(t, ChangeType(row[pInfo.Name], pInfo.PropertyType), null);
}
catch(Exception ex){
throw ex;
}
}
}
list.Add(t);
}
return list;
}
return null;
}
/// <summary>
/// 得到指定SQL执行的结果并返回结果的实体类集合对象
/// 本方法综合了 ConvertDataTableToEntityCollections/ExecuteSQL 方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql">待执行的SQL</param>
/// <returns></returns>
protected List<T> GetEntityConllectionsFromSQL<T>(string sql)
where T : class, new()
{
if (string.IsNullOrEmpty(sql))
return null;
else
return ConvertDataTableToEntityCollections<T>(ExecuteSQL(sql));
}
/// <summary>
/// 得到指定类型公开的属性
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private PropertyInfo[] GetTypePropertyInfo(Type type)
{
PropertyInfo[] infos = null;
//锁定防止 多次 ADD 进缓存
lock (_PROPERTYINFO_READ_LOCK)
{
_PropertyInfoCache.TryGetValue(type, out infos);
if (infos == null)
{
infos = type.GetProperties();
_PropertyInfoCache.Add(type, infos);
}
}
return infos;
}
/// <summary>
/// 类型转换
/// FROM: http://www.cnblogs.com/cnee5/archive/2006/05/21/405403.html
/// </summary>
/// <param name="value"></param>
/// <param name="conversionType"></param>
/// <returns></returns>
public object ChangeType(object value, Type conversionType)
{
//INFO 对DBNull类型特殊处理
if (Convert.IsDBNull(value))
{
//非可空类型的值类型处理
if (!(conversionType.IsGenericType &&
conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))))
return Activator.CreateInstance(conversionType);
else
return null;
}
//可空类型类型处理
if (conversionType.IsGenericType &&
conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
return null;
System.ComponentModel.NullableConverter nullableConverter
= new System.ComponentModel.NullableConverter(conversionType);
conversionType = nullableConverter.UnderlyingType;
}
return Convert.ChangeType(value, conversionType);
}
private object _PROPERTYINFO_READ_LOCK = new object();
/// <summary>
/// 实体类属性缓存
/// </summary>
private static Dictionary<Type, PropertyInfo[]> _PropertyInfoCache = new Dictionary<Type, PropertyInfo[]>();
#endregion
}
使用GetEntityConllectionsFromSQL<T>(string sql)方法直接执行一个返回DataTable的ExecuteSQL(sql)方法来快捷完成转换. 在不用ORM框架的情况下就是简单的取得数据变成实体对象集合时很方便, 当然前提是不使用强类型数据集.
[原创]小型新闻门户网站建设经验说法
作者:tupunco 日期:2009-03-09
去年十一前后, 公司接到一个事业单位网站的单子, 规划编码工作由我来完成, 单子完成后写了本文的提纲, 但由于气候天冷的缘故没有写完, 今天正好天气回暖了, 一个多月的阴雨天也结束了, 来把提纲的内容整理一下.
网站的需求是客户提供的, 其实就是列了一个网站的大致导航栏目划分, 根据客户要求, 站点需要 PHP 来实现, 因为客户的主机用的是其管理部门的公共服务器, 服务器使用 Apache + MySQL 作为Web平台. 为了快速的构建站点, 使用了国内开源免费的 CMS 系统来完成, 名称为<DreamArticle>, 此 CMS 系统授权并不像"织梦CMS"或"帝国CMS"一样对政府事业单位需要收费, 而是在保留版权声明的前提下对所有用户都免费, 一个不错的选择, 功能也基本满足客户需要, 但还是开发了一部分新的功能, 并且修改了一部分系统的 BUG. 作为对此 CMS 系统的支持, 向作者提供了BUG 列表和大部分的二次开发代码, 之后作者在新得版本内对 BUG 做了修改. 选择 CMS 系统的时候对国外多个开源 CMS 系统做了测试, 但可以快速达到客户要求, 并且在使用简便性上占优势的几乎为零, 可能也是自己使用理解上的欠缺, 要不就是对 CMS 系统理解有限, 国内多个大型成熟的 CMS 系统的调查发现, 大部分使用都不错, 符合国情, 但授权方面是一个问题, 毕竟大家都要吃饭.
站点美工设计完成的稿经过客户评价后, 转成了 WEB 页面, 页面全部使用了DIV+CSS方式来布局, 期间遇到了好多问题, 下面有路罗列, 但还是从需求方面来说.
[转载翻译]ASP.NET MVC RC1学习向导
作者:tupunco 日期:2009-02-17
ASP.NET RC1 在春节期间发布(一月末), 按 Scott Guthrie 博客的说法ASP.NET MVC 只有一个RC版, 正式版的将在二月份发布, 通常RC版的软件已经相当的接近正式版, 所以可以使用在项目开发上, 也可以说 ASP.NET MVC 已经定型了, 正式版只可能有某些Bug修复和细微的改进. 我从J2EE上的Struts, 到国人的EasyJf, 从 ROR 到 CakePHP , 看过好多的 MVC 框架的使用方法, 看到了很多, 但没有真正明白这些东西为啥要这么开发, 这么使用, 为什么非要 Controller 加很多Action. 直到后来看到 Castle.MonoRail, 发现使用方法好眼熟, 才想起ROR中的一个类一个Controller, 一个方法是一个Action的规律, 也是自己对Ruby语言不熟悉, 也没有好好的看看MVC模式的说明, 也没有搞明白MVC各部分的关系, 才造成自己长时间在MVC方面没有具体的进步. 现在才明白规律就是ROR是基本结构的模式, 才有的EasyJf/CakePHP/MonoRail..., 还有国内的几个PHP MVC 框架, 设计大同小异, 虽然内部设计大有不同, 但使用方法, 基本类似.
后来有微软发布自己MVC框架的消息, 在经过一天的"放鸽子"之后, 第二天总算见到了技术预览版(CTP)的ASP.NET MVC, 看介绍发现就是ROR的开发结构方式, 一个类一个Controller, 一个控制器可以包含多个Action, Controller/Action拦截器, 路由器一个都没有少, 只是模型部分没有单独实现, 不过 .NET 上纷繁的第三方ORM框架和微软的LINQ to SQL/ADO.NET 实体框架功能已经相当强大. 经过五个预览版本的测试, 期间经过了多次的大的代码重构, ASP.NET MVC不断的优化, 后来更是直接集成JQuery框架, 再到Beta版, ASP.NET MVC 设计已经相当完美. 元旦前后就有消息说是一月份发布RC版, 但直到一月三十号才发布, 还真是一月份发布, 不过自己看到已经到春节后了.
下面是一篇文章的翻译, 文章给出了一个学习ASP.NET MVC的渐进文章列表, 这里试着翻译一下. 此作者的博客有一个<ASP.NET MVC 技巧集锦>的系列文章, 自己也在博客推荐过. 这篇文章过年上班的时候就看到了,一致在Live Writer内放着也没有翻译完,今天晚上翻译绝大部分,正确与否也没有办法,毕竟英语本来就菜.
[编译] ASP.NET 安全教程
作者:tupunco 日期:2009-02-12
记得 ASP.NET 官方网站有一系列文章<Data Access Tutorials>, 成了自己的ASP.NET数据库开发的入门教程, 此博客也做过推荐. 其不仅仅讲述了ASP.NET上数据访问方面技术, 还有部分内容介绍"三层结构/站点地图/控件使用", 使自己受益匪浅. 内容现在已经更新了60多篇, 这几天发现一个中文翻译更全面的网站, 本文的末尾提供链接.
现在ASP.NET官方网站又发布了一个系列教程<ASP.NET Security Tutorials>, 粗略的看了一下, 内容讲解的很全面, 不单单讲解, ASP.NET 中"安全控件/Membership/Roles"的使用, 还讲解了ASP.NET的安全机理, 由浅到深, 让 ASP.NET 开发人员更好的明白 ASP.NET 中的安全设计.
文章的简介翻译: 欢迎能看本 ASP.NET 安全系列教程, 本教程探讨ASP.NET 应用程序上的"表单验证/页面或功能的授权访问/用户帐户管理"方面的技术.



