随笔 - 38  文章 - 2  评论 - 42 
  2009年7月3日
     摘要: POP协议简介本文简要说明了通过POP3协议收取邮件、MIME邮件的解码的原理;针对收取和MIME解码,提供了两个实用的PHP类,并提供了使用的样例。分为邮件收取、MIME解码两个部分。这里我们先向您介绍邮件的收取,解码部分会在以后的文章中为各位详细的介绍,敬请关注。 现在Internet上最大的应用应该是非Email莫属了,我们每天都习惯于每天通过Email进行交流,各大网站也几乎都推出了自己的... 阅读全文
posted @ 2009-07-03 15:00 xin478 阅读(55) | 评论 (0)编辑
  2009年6月23日

老早想换硬盘,看着ST11的固件门,原想12该不会有问题,上网一看,傻了,各大硬件论坛都有说重映射的问题,


具体的baidu,google下,多了去了。前几天同学要买盘,没禁住平均过100M的速度诱惑,买了一个,回来装个系统,一看,high到


从11的固件问题到12的重映射问题,希捷着实让我失望了,而且希捷官方至今没有回复。原引网络上的话,珍惜数据,远离希捷

顺便普及下什么是重映射:

重映射扇区
硬盘的数据密度很大,在生产过程中不可避免地会产生缺陷,同时在使用过程中,那些不稳定的扇区也会逐渐老化而产生数据读写错误,这些缺陷和不稳定扇区会严 重威胁硬盘数据的安全,为此,硬盘设计了两个坏道表来处理这些有缺陷的扇区,他们就是P-list和G-list.,它们用于记录硬盘的缺陷扇区的情况, 使硬盘工作时不会在缺陷扇区里读写数据,防止数据损坏。 坏道的产生可以分为两种情况:一是生产过程中产生的缺陷扇区,二是使用过程的产生的缺陷扇区,硬盘设计两个坏道表就是用于分别识别和处理硬盘的两种不同的坏道的。
P-list
P-list我们一般称为工厂坏道表,严格来说应该称为永久坏道表或原始坏道表,它是用于记录工厂生产过程中产生的坏道的,坏道加入P-list不会影响硬盘的读写性能。
G-list
G-list称为增长坏道表,用于记录硬盘使用过程中由于磁介质性能变弱而引起的坏道,并将坏扇区重定向到好扇区,坏道加入G-list对该扇区的读写速度是有影响的。
下面我们就谈谈硬盘对P-list和G-list的两种不同的处理方式。

下图是硬盘的扇区分配情况,硬盘的全部扇区可以划分为三个方面,固件区、工作区和保留扇区;其中固件区和保留扇区普通用户是没办法操作的,硬盘的实际扇区 数比我们看到的硬盘标签上标定的要大,其中一部份用于存储硬盘的固件,一部分是用户存储数据的区域(工作区),也就是硬盘标定容量的扇区,剩下的就是保留 区,实际上硬盘上并不会物理划出一个保留区域,只是在工厂生产时标定了全部的有效扇区,而硬盘的容量是小于其实际扇区总数的,在固件里定义了硬盘的容量, 超过硬盘容量的那些扇区我们就姑且把它称为保留扇区。

上图中蓝色为固件区,白色为工作区,绿色为保留扇区,红色为坏扇区,图示表示的是坏扇区还没有加入P-list和G-list的情况。大家可以看到,工作 区的LBA是从固件区后的LBA0一直连续标定到硬盘的最大工作扇区LBAmax,LBAmax以后的扇区就是保留扇区。
下图是坏扇区被加入P-list的情况:

从图中可以看出,坏道被加入P-list后,硬盘不会再读写该扇区,而是在将原读写该扇区的操作顺延到度写坏扇区的下一个扇区,该扇区以后的所有扇区的 LBA值都发生了改变,原来保留扇区的一个扇区成为了硬盘的LBAmax,所以坏道被加入P-list后,硬盘需要进行一次厂家低格。
下图是坏道加入G-list的情况:

  从图中可以看出,坏道加入G-list后,当硬盘需要读坏道所在的扇区时,会被重定位到保留扇区中的一个扇区,硬盘工作区的其它扇区不会受影响,由于保留 扇区在硬盘的内道,读写速度慢,同时由于该扇区会导致硬盘的数据存储从物理上来说不连续了,当磁头读取该扇区的数据时需要移动较远的距离,代替坏扇区后, 该LBA的读写速度会慢一些,所以我们说坏道加入G-list后会影响硬盘的读写速度。

 

posted @ 2009-06-23 11:02 xin478 阅读(21) | 评论 (0)编辑
  2009年3月30日

 

今天下午想让一张向y轴平铺的背景PNG图片实现透明,查了一些方法,又试了很久,如果用滤镜方法的话可以实现透明,但宽会变,而且我要控制的是body的背景,再再再是它。。。不会平铺的了。这样自然是不能满足我的想法,而我风格的布局又要用到PNG图片,怎么办?

于是我又试了一个JS的,结果效果也不出来。我11号找了两个方法用于实现PNG图片在IE6下透明,Unit PNG Fix用了一下,没有成功,应该是我的英文差,看不全这些东西。后来又试了一个IE PNG Fix V1.0,但发现它不支持IE6下PNG透明平铺背景图片。正在沮丧时,忽然看到IEPNGFix v2.0 Alpha — with background position/repeat! 问题从而解决.

插入的图片,img标签

背景图片,background


使用简介:页面标签使用behavior:url(”iepngfix.htc”);来调用外部包含js、css的iepngfix.htc文件來修正PNG alpha 透明度。

测试实例1:http://www.css88.com/demo/IE6_bug/IE6_bug_2/IE6_bug_2.html

测试实例2(官方,e文):http://www.css88.com/demo/IE6_bug/IE6_bug_2/iepngfix.html

支持<img src=”">元素。

支持的背景PNG图像(不像许多其他的脚本! )

支持CSS1背景重复和位置(通过可选插件)

背景图像可以被界定内置或在外部的样式表。

自动处理改为钢骨混凝土/背景通过正常的JavaScript (例如鼠标悬停翻车) -没有特殊的编码需要。

改变支持包括CSS的’类别’的变化因素。

采用自动变通的要素巴布亚新几内亚背景因素。

微小的脚本(快速下载) 。

领有牌照的下一个自由软件许可证。

如何使用

Follow these simple steps to add this to your page:遵循这些简单的步骤,将此添加到您的网页:

复制并粘贴iepngfix.htc和blank.gif到您的网站文件夹中。

复制并粘贴到您的网站的CSS或HTML :


这的CSS选择器必须包含标签/内容要巴布亚新几内亚支持-基本上,给它一个逗号分隔的清单上的标签使用。它还必须包括正确的路径。宏达相对H TML文件的位置(不相对的CSS文件! ) 。 例如,你可能看起来像这样:


如果您的网站使用的子文件夹,打开。宏达文件在文本编辑器如Windows记事本,并改变blankImg变数,包括正确道路blank.gif像这样:

IEPNGFix.blankImg = ‘/images/blank.gif’;

同样的路径是相对的HTML文件。否则,你将看到一个“破碎的形象”的图形!

如果你想支持CSS1背景重复和背景位置,请务必包括附加。 js文件在您 :


否则,背景图片可以工作,但不会重复或立场。

可以舒舒服服地坐下欣赏! 也许考虑捐助,以支持该脚本的发展如果你喜欢您所看到的,因为我花了数百小时的开发,测试和支持它: ) 。 另外,我一定会感谢一个入链接您的网站到地雷!

官方原文:http://www.twinhelix.com/css/iepngfix/

如果你有兴趣了解更多信息或另一种方法激活的脚本保持审定的CSS相容性,看到源代码演示文件。

———————————————————————–

如何解决共同问题

1.我已经贴在CSS中,但我pngs并不透明!

请务必记住,路径个文件是相对于HTML文件,而不是的CSS文件(如的CSS背景图像) 。 如果您想要测试的路径,插入: alert(’This works!’);进入。宏达文件。

2.它的工程离线而不是在线。

第一次尝试解压这个预设的示范和上传到Web服务器原样。 如果它不工作,您可能有一个MIME类型的问题。您必须确保您的服务器发送正确的MIME类型“文本/的X组成部分”的。宏达文件。 尝试之一,这两个容易修复程序:

3.上传的。 “ htaccess ”文件,该脚本的下载压缩到Web服务器上,这将使阿帕奇发出正确的MIME类型。

4.而不是所谓的“ IEPNGFIX.HTC ”从你的CSS ,上传IEPNGFIX.PHP的同一文件夹中,并呼吁不是,这也发出了正确的MIME类型。

5.我pngs是透明的,但有一个有趣的边界或红色的“ X ”图标。

检查blankImg变量设置正确的。宏达文件,再次相对应的HTML文件,载PNGs 。

图像扭曲,或此脚本休息,我的网页布局。

当适用于图像没有设置方面,该脚本将尝试与“猜测”正确的图像尺寸和适用的。如果它获得做错了什么,给您一个明确的图像width 。

链接或表单元素一个png’d元素是不能点击。

测试实例1:http://www.css88.com/demo/IE6_bug/IE6_bug_2/IE6_bug_2.html

测试实例2(官方,e文):http://www.css88.com/demo/IE6_bug/IE6_bug_2/iepngfix.html

posted @ 2009-03-30 17:37 xin478 阅读(208) | 评论 (0)编辑
  2009年3月24日

神奇的windows7,居然装不上vs2008标准版,改装了团队版,嘿,行了。因为还有做php开发,接着装上vs.php,安装没有什么问题,可以运行php项目就不行了,提示Cannot Create Project,说什么打不开.phpproj,啊,神啊,接下来的日子用emedit搞了几天,不是说emedit不好,做项目的话,实在是不行,debug的没有也就算了,代码感知也没有,好惨啊。今天上vs.php官网载了2.7版的,还是不行。但是看到有人提出了,一试,对头,记下。

 

Hi,
Just installed the evaluation version for VS 2005. The installation was error free. When I try to create a new PHP project I get an error similar to:

Fail to create project. The application for project 'C:\Program Files\Jcx.Software\VS.Php\2005\2.5\ProjectTemplates\empty_php5_project.phpproj' is not installed. Make sure the application for the project type (.phpproj) is installed.

 

 

On September 1st, 2008 juanc said:

Try this from the command line:

devenv /resetskippkgs

 

对了,再顺便展示一下,个人觉得vs.php比那个zendstudio好多了,那个卡死,功能也不见得比这个好多少,vs稳定多了。

 

自定义类感知,该类在项目中的另一个文件里

注释提示,从类到方法,参数,返回值,全有

开发大项目必备的debug

那个2.7居然还支持silverlight开发,现在php中用silverlight方便多了。随便说一下,vs.php是要钱的,不要钱的话也是有的,方法自己找咯。

posted @ 2009-03-24 09:38 xin478 阅读(360) | 评论 (8)编辑
  2009年3月20日

 

单位里用windows7挺爽,家里也装上了,一切都还正常,玩了2天,欲装vs2008,岂料vs2008标准版还装不上,一运行安装程序就退出,没办法,改装团队版,装到一半,熟悉的界面出现啦,beta版的,难免的事,贴上做个纪念哦。

 

posted @ 2009-03-20 20:20 xin478 阅读(143) | 评论 (1)编辑

一直以为mysql随机查询几条数据,就用

SELECT * FROM `tableORDER BY RAND() LIMIT 5


就可以了。
但是真正测试一下才发现这样效率非常低。一个15万余条的库,查询5条数据,居然要8秒以上

查看官方手册,也说rand()放在ORDER BY 子句中会被执行多次,自然效率及很低。

You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times.


搜索Google,网上基本上都是查询max(id) * rand()来随机获取数据。

SELECT * 
FROM `tableAS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2 
WHERE t1.id >= t2.id 
ORDER BY t1.id ASC LIMIT 5;

 

但是这样会产生连续的5条记录。解决办法只能是每次查询一条,查询5次。即便如此也值得,因为15万条的表,查询只需要0.01秒不到。

上面的语句采用的是JOIN,mysql的论坛上有人使用

SELECT * 
FROM `table
WHERE id >= (SELECT FLOORMAX(id) * RAND()) FROM `table` ) 
ORDER BY id LIMIT 1;

 

我测试了一下,需要0.5秒,速度也不错,但是跟上面的语句还是有很大差距。总觉有什么地方不正常。

于是我把语句改写了一下。

SELECT * FROM `table
WHERE id >= (SELECT floor(RAND() * (SELECT MAX(id) FROM `table`)))  
ORDER BY id LIMIT 1;

这下,效率又提高了,查询时间只有0.01秒

最后,再把语句完善一下,加上MIN(id)的判断。我在最开始测试的时候,就是因为没有加上MIN(id)的判断,结果有一半的时间总是查询到表中的前面几行。
完整查询语句是:

SELECT * FROM `table
WHERE id >= (SELECT floorRAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`)) + (SELECT MIN(id) FROM `table`)))  ORDER BY id LIMIT 1;


SELECT * 
FROM `tableAS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`))+(SELECT MIN(id) FROM `table`)) AS id) AS t2 
WHERE t1.id >= t2.id 
ORDER BY t1.id LIMIT 1;

 

最后在php中对这两个语句进行分别查询10次,
前者花费时间 0.147433 秒
后者花费时间 0.015130 秒
看来采用JOIN的语法比直接在WHERE中使用函数效率还要高很多。

posted @ 2009-03-20 15:59 xin478 阅读(43) | 评论 (0)编辑
  2009年2月16日
 采集的数据中,原来是日文的"ブリーチ, Burīchi",html代码中是ブリーチ, Burīchi,这样做有个好处,网页就不一定要像UTF-8这样的编码,但是想要原来的文字,php转半天过不来,后来发现是未指定编码集,用
html_entity_decode("ブリーチ, Burīchi",ENT_NOQUOTES,'UTF-8')

就可以了。
其实,原理也很简单,这个是模拟这个函数的功能,PHP4的话,就一定要用了
function unhtmlentities($string
{
 
// replace numeric entities
 $string = preg_replace('/([0-9a-f]+);/ei', 'uchr(hexdec("\\1"))', $string);
 
$string = preg_replace('/&#([0-9]+);/e', 'uchr("\\1")', $string);
 
// replace literal entities
 $trans_tbl = get_html_translation_table(HTML_ENTITIES);
 
$trans_tbl = array_flip($trans_tbl);
 
return strtr($string, $trans_tbl);
}

function uchr ($codes) {
 
if (is_scalar($codes)) $codes= func_get_args();
 
$str= '';
 
foreach ($codes as $code$str.= html_entity_decode('&#'.$code.';',ENT_NOQUOTES,'UTF-8');
 
return $str;
}
unhtmlentities(
"ブリーチ, Burīchi");


用.net实现下编码
   Byte[] bComments = Encoding.UTF8.GetBytes("一ンブル????中文");
   
char[] cComments = Encoding.UTF8.GetChars(bComments);
   
   StringBuilder charBuilder 
= new StringBuilder();   
   
foreach(char c in cComments)
   {
    
if(c > '\u0800')
    {
     charBuilder.Append(
"&#");
     charBuilder.Append((
int)c);
    } 
    
else
    {
     charBuilder.Append(c);
    }
   }
   Response.Write(charBuilder.ToString());


这段代码的作用是将所有的中文、韩文、日文字符通过硬编码输出成为html实体。而Html实体是不受ResponseEncoding和页面编码集影响的。

说明:

\u0800 以上的为中、韩、日字符。

中文的范围:\u4e00 - \u9fa5,日文在\u0800 - \u4e00,韩文为\u9fa5以上。 
posted @ 2009-02-16 16:20 xin478 阅读(262) | 评论 (0)编辑
  2008年12月29日
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _Default : Page 
{
    
protected void Page_Load(object sender, EventArgs e)
     {

     }

    
#region OnPreInit 第一步
    
protected override void OnPreInit(EventArgs e)
     {
        
//检查 IsPostBack 属性来确定是不是第一次处理该页。

        
//创建或重新创建动态控件。

        
//动态设置主控页。

        
//动态设置 Theme 属性。

        
//读取或设置配置文件属性值。

        
//注意  
        
//如果请求是回发请求,则控件的值尚未从视图状态还原。如果在此阶段设置控件属性,则其值可能会在下一事件中被重写。


        
base.OnPreInit(e);
     }
    
#endregion

    
#region OnInit 第二步
    
protected override void OnInit(EventArgs e)
     {
        
//在所有控件都已初始化且已应用所有外观设置后引发。使用该事件来读取或初始化控件属性。
        base.OnInit(e);
     }
    
#endregion

    
#region OnInitComplete 第三步
    
protected override void OnInitComplete(EventArgs e)
     {
        
//由 Page 对象引发。使用该事件来处理要求先完成所有初始化工作的任务。

        
base.OnInitComplete(e);
     }
    
#endregion

    
#region PreLoad 第四步
    
protected override void OnPreLoad(EventArgs e)
     {
        
//如果需要在 Load 事件之前对页或控件执行处理,请使用该事件。 

        
//在 Page 引发该事件后,它会为自身和所有控件加载视图状态,然后会处理 Request 实例包括的任何回发数据。

        
base.OnPreLoad(e);
     }
    
#endregion 

    
#region OnLoad 第五步
    
protected override void OnLoad(EventArgs e)
     {
        
//Page 在 Page 上调用 OnLoad 事件方法,然后以递归方式对每个子控件执行相同操作,如此循环往复,直到加载完本页和所有控件为止。
        
//使用 OnLoad 事件方法来设置控件中的属性并建立数据库连接。

        
base.OnLoad(e);
     }
    
#endregion

    
#region 控件事件 第六步
    
protected void Button1_Click(object sender, EventArgs e)
     {
        
//用这些事件来处理特定控件事件,如 Button 控件的 Click 事件或 TextBox 控件的 TextChanged 事件。

        
//注意  
        
//在回发请求中,如果页包含验证程序控件,请在执行任何处理之前检查 Page 和各个验证控件的 IsValid 属性。


     }
    
#endregion

    
#region OnLoadComplete 第七步
    
protected override void OnLoadComplete(EventArgs e)
     {
        
//对需要加载页上的所有其他控件的任务使用该事件。

        
base.OnLoadComplete(e);
     }
    
#endregion

    
#region OnPreRender 第八步
    
protected override void OnPreRender(EventArgs e)
     {
        
//在该事件发生前:

        
//Page 对象会针对每个控件和页调用 EnsureChildControls。 

        
//设置了 DataSourceID 属性的每个数据绑定控件会调用 DataBind 方法。有关更多信息,请参见下面的数据绑定控件的数据绑定事件。

        
//页上的每个控件都会发生 PreRender 事件。使用该事件对页或其控件的内容进行最后更改。

        
base.OnPreRender(e);
     }
    
#endregion 

    
#region SaveStateComplete 第九步
    
protected override void OnSaveStateComplete(EventArgs e)
     {
        
//在该事件发生前,已针对页和所有控件保存了 ViewState。将忽略此时对页或控件进行的任何更改。

        
//使用该事件执行满足以下条件的任务:要求已经保存了视图状态,但未对控件进行任何更改。

        
base.OnSaveStateComplete(e);
     }
    
#endregion

    
#region Render 第十步
    
//Render
    
//这不是事件;在处理的这个阶段,Page 对象会在每个控件上调用此方法。所有 ASP.NET Web 服务器控件都有一个用于写出发送给浏览器的控件标记的 Render 方法。

    
//如果创建自定义控件,通常要重写此方法以输出控件的标记。不过,如果自定义控件只合并标准的 ASP.NET Web 服务器控件,不合并自定义标记,则不需要重写 Render 方法。有关更多信息,请参见开发自定义 ASP.NET 服务器控件。

    
//用户控件(.ascx 文件)自动合并呈现,因此不需要在代码中显式呈现该控件。

    
#endregion

    
#region OnUnload 第十一步       
    
protected override void OnUnload(EventArgs e)
     {
        
//该事件首先针对每个控件发生,继而针对该页发生。在控件中,使用该事件对特定控件执行最后清理,如关闭控件特定数据库连接。

        
//对于页自身,使用该事件来执行最后清理工作,如:关闭打开的文件和数据库连接,或完成日志记录或其他请求特定任务。

        
//注意  
        
//在卸载阶段,页及其控件已被呈现,因此无法对响应流做进一步更改。如果尝试调用方法(如 Response.Write 方法),则该页将引发异常。


        
base.OnUnload(e);
     }
    
#endregion
}
posted @ 2008-12-29 21:07 xin478 阅读(33) | 评论 (0)编辑
  2008年10月16日

当你在vs中设置的字体不支持中文时(如consolas),vs使用一个文件来决定使用什么中文字体。
 这个文件就是:
 %userprofile%\application data\microsoft\visual studio\9.0\vsfontlk.dat

 这个文件是文本格式,utf16编码,带bom。使用一个合适的编辑器打开,看这一行
 0804|NSimSun|新宋体
 0804是16进制,对应10进制是2052,也就是chinese PRC的lang id了。后面是字体。
 把这行改成
 0804|微软雅黑


 重新打开vs,字体变啦!(只是新中文看起来较小)

posted @ 2008-10-16 10:06 xin478 阅读(153) | 评论 (1)编辑
  2008年10月15日
最近研究权限,需要设计一种适合二次开发的可扩展权限控制方法,为未知的权限进行设计。找到一点资料,没来得及看,在这里存档。
相关连接:
关于权限设计的探讨(寻求数据库设计者和开发人员的帮助)
自我消遣之一:四维权限管理模型
自我消遣之二:权限管理与访问控制概要设计 
引用
关于权限设计的探讨 


但凡涉及多用户不同权限的网络或者单机程序,都会有权限管理的问题,比较突出的是MIS系统。 

下面我要说的是MIS系统权限管理的数据库设计及实现,当然,这些思路也可以推广开来应用,比如说在BBS中用来管理不同级别的用户权限。 

权限设计通常包括数据库设计、应用程序接口(API)设计、程序实现三个部分。 

这三个部分相互依存,密不可分,要实现完善的权限管理体系,必须考虑到每一个环节可行性与复杂程度甚至执行效率。 

我们将权限分类,首先是针对数据存取的权限,通常有录入、浏览、修改、删除四种,其次是功能,它可以包括例如统计等所有非直接数据存取操作,另外,我们还可能对一些关键数据表某些字段的存取进行限制。除此,我想不出还有另外种类的权限类别。 

完善的权限设计应该具有充分的可扩展性,也就是说,系统增加了新的其它功能不应该对整个权限管理体系带来较大的变化,要达到这个目的,首先是数据库设计合理,其次是应用程序接口规范。 

我们先讨论数据库设计。通常我们使用关系数据库,这里不讨论基于Lotus产品的权限管理。 

权限表及相关内容大体可以用六个表来描述,如下: 
1 角色(即用户组)表:包括三个字段,ID,角色名,对该角色的描述; 
2 用户表:包括三个或以上字段,ID,用户名,对该用户的描述,其它(如地址、电话等信息); 
3 角色-用户对应表:该表记录用户与角色之间的对应关系,一个用户可以隶属于多个角色,一个角色组也可拥有多个用户。包括三个字段,ID,角色ID,用户ID; 
4 限制内容列表:该表记录所有需要加以权限区分限制的数据表、功能和字段等内容及其描述,包括三个字段,ID,名称,描述; 
5 权限列表:该表记录所有要加以控制的权限,如录入、修改、删除、执行等,也包括三个字段,ID,名称,描述; 
6 权限-角色-用户对应表:一般情况下,我们对角色/用户所拥有的权限做如下规定,角色拥有明令允许的权限,其它一律禁止,用户继承所属角色的全部权限,在此范围内的权限除明令禁止外全部允许,范围外权限除明令允许外全部禁止。该表的设计是权限管理的重点,设计的思路也很多,可以说各有千秋,不能生搬硬套说某种方法好。对此,我的看法是就个人情况,找自己觉得合适能解决问题的用。 

先说第一种也是最容易理解的方法,设计五个字段:ID,限制内容ID,权限ID,角色/用户类型(布尔型字段,用来描述一条记录记录的是角色权限还是用户权限),角色/用户ID,权限类型(布尔型字段,用来描述一条记录表示允许还是禁止) 

好了,有这六个表,根据表六,我们就可以知道某个角色/用户到底拥有/禁止某种权限。 

或者说,这么设计已经足够了,我们完全实现了所需要的功能:可以对角色和用户分别进行权限定制,也具有相当的可扩展性,比如说增加了新功能,我们只需要添加一条或者几条记录就可以,同时应用程序接口也无须改动,具有相当的可行性。但是,在程序实现的过程中,我们发现,使用这种方法并不是十分科学,例如浏览某个用户所拥有的权限时,需要对数据库进行多次(甚至是递归)查询,极不方便。于是我们需要想其它的办法。使用过Unix系统的人们都知道,Unix文件系统将对文件的操作权限分为三种:读、写和执行,分别用1、2、4三个代码标识,对用户同时具有读写权限的文件被记录为3,即1+2。我们也可以用类似的办法来解决这个问题。初步的想法是修改权限列表,加入一个字段:标识码,例如,我们可以将录入权限标识为1,浏览权限标识为2,修改权限标识为4,删除权限标识为8,执行权限标识为16,这样,我们通过权限累加的办法就可以轻易的将原本要分为几条记录描述的权限放在一起了,例如,假定某用户ID为1,库存表对应的限制内容ID为2,同时规定角色类型为0、用户类型为1,我们就可以将该用户具有录入、浏览、修改、删除库存表的权限描述为:2,15,1,1。 

确实很简单,不是吗?甚至还有更过激的办法,将限制内容列表也加上一列,定义好标识码,这样,我们甚至可以用简单的一条记录描述某个用户具有的对全部内容所具有的全部权限了。当然,这样做的前提是限制内容数量比较小,不然,呵呵,2的n次方递增起来可是数量惊人,不容易解析的。 

从表面上看,上述方法足以达到实现功能、简化数据库设计及实现的复杂度这个目的,但这样做有个弊端,我们所涉及的权限列表不是相互独立而是互相依赖的,比如说修改权限,其实是包含浏览权限的,例如,我们可能只是简单的设置用户对库存表存取的权限值为录入+修改+删除(1+4+8=13),但事实上,该用户具有(1+2+4+8=15)的权限,也就是说,在这种方案中,13=15。于是当我们调用API询问某用户是否具有浏览权限时,就必须判断该用户是否具有对该数据表的修改权限,因此,如果不能在程序中固化权限之间的包含关系,就不能利用应用程序接口简单的做出判断。但这与我们的目的“充分的可扩展性”矛盾。 

这个问题如何解决?我想到了另外一种设置标识码的方法,那就是利用素数。我们不妨将录入、浏览、修改、删除、执行的基本标志码定为2,3,5,7,11,当遇到权限互相包含的时候,我们将它的标识码设定为两个(或多个)基本标志码的乘积,例如,可以将“修改”功能的标志码定为3*5=15,然后将所有的权限相乘,就得到了我们需要的最终权限标识值。这样,我们在询问用户是否具有某项权限的时候,只需要将最终的值分解成质因子,例如,我们可以定义一个用户具有录入+修改+删除库存表的权限为 2*15*7=2*3*5*7,即表示,该用户具有了对库存表录入+浏览+修改+删除权限。 

当然,对权限列表我们使用上述方法的前提是权限列表记录条数不会太多并且关系不是十分复杂,否则,光是解析权限代码就要机器忽悠半宿:) 

我希望以上的分析是正确且有效的(事实上,我也用这些的方法在不止一套系统中实现),但无论如何,我觉得如此实现权限管理,只是考虑了数据库设计和应用程序接口两部分内容,对于实现,还是显得很费劲。因此,我恳请有过类似设计、实现经验的同志们提出建设性的意见和修改建议。 


另外,有朋友提到: 

权限设计需求: 
1、用以实现对界面对象(如菜单、按钮)的权限控制。简称对象级权限 
2、用以实现对数据能否被某一个用户修改、删除的权限控制。建成数据级权限 

对象级权限基本上大家都实现过,数据级权限比较特殊,它能够实现数据的权限分配 
例如:A用户可以将其产生的数据分配给B用户修改,给C用户删除的权限。 

数据级权限的要点就是要在表中增加一个字段用以保存数据所有者的用户标识。 


我的想法是这样: 

数据级权限的管理和对象权限有比较大的区别,它不容易使用一条语句精确的描述,Notes的做法是保存每个文档的所有者标识,在关系型数据库中也可以使用相同(或者类似)的办法解决,但情况要复杂一些。 

如果要实现对单条数据的存取控制,恐怕只能用流程来实现,这种手段在OA系统中比较常见,比如将A写的某个公文文档移交给B审批,这时B获得的只是对所有者A某一个而不是所有文档的审批权限。虽然也是赋予了B某种权限,但事实上这不属于上文所讨论的权限管理范围,而只是业务流程。 

但是我们考虑,在非公文流程的大部分系统中,只是简单的设定:用户B具有对所有者A的文档具有修改权限,而这种定义,是可以在上文描述的数据库进行描述并实现的。 

事实上,在关系型数据库中,要对某个用户对表的存取权限精确到记录是不可能的,这只能在程序中使用聪明巧妙或者笨办法解决,当然,与之相关的是业务流程的设计。很多OA系统自称能自定义流程,大体也就是能通过巧妙的数据库设计,将流程运作的全过程记录在数据库中。 

基于Lotus的OA系统理论上可以定义某个用户对某个文档的存取权限,但实际操作中,为了减轻开发人员的负担,都只是定义了角色对某个数据库的存取权限,而将剩下的内容交由前端控制,这和我们在ASP程序中习惯使用SA登录SQL Server是一样的。 

业务流程的实现方法本质上和操作权限无关,它将是另外一个讨论话题。 

以上只是个人浅见,请大家批评指正!

posted @ 2008-10-15 15:53 xin478 阅读(106) | 评论 (0)编辑