关于Single Sign On的一些设想

单点登陆,始终有其吸引力。然而就实现而言,结合栋力无限现有情况,比较困难。

主要原因在于栋力无限门户下属各个子站点,不仅有自己开发的,也有直接使用的他人开发的开源项目的。特别对于后者,如果花大力气,对其进行修改,使其使用我们自身的passport,一旦其有所升级、打patch等变动,就会导致大量的重复劳动。

在现有条件下,是否存在一种花费最小代价的Single Sign On实现方式?换句话说,使得插接到passport的各个子站点,所作的修改尽量的少?

最近有一个设想,就是如果想要尽可能少的修改各个子站点,实现passport的通用性,唯有通过模拟各个子站点自身的登陆行为。

法1、填写username password,并post到其form的action链接里面;
法2、直接在用户本机写入cookie文件;

两种方式其实都可以。具体一点说:

第一阶段试验目标:

1、登陆passport.dormforce.net后,passport修改用户登陆状态信息为已登录(并以Web Services或其他方式对外公开)。

2、passport调用放在各个子站点自身程序里的一个模板页面,那个页面实现的唯一功能就是采用法1或法2模拟登陆本子站点,若有必要,在这里,还可以增加对Session,Application等变量的支持;(由于开源,可以跟踪入内,找到其登陆代码,copy过来修改一下即可)(调用的时候,最好使用AJAX等技术后台加载,防止页面频繁刷新,提高用户体验)

3、passport调用放在各个子站点自身程序里的一个模板页面,那个页面实现的唯一功能就是采用法1或法2登出本子站点,若有必要,在这里,还可以增加对Session,Application等变量的支持;(由于开源,可以跟踪入内,找到其登出代码,copy过来修改一下即可)(调用的时候,最好使用AJAX等技术后台加载,防止页面频繁刷新,提高用户体验)

至此,应该能够实现在passport登陆后,畅通访问各个子站点;在passport登出后,退出所有子站点;

第二阶段试验目标:

1、通过用户登陆状态信息等其他验证安全的手段,确保放在各个自站点的模拟登陆页面安全。

2、修改各个子站点的登陆和登出链接,指向passport

至此,应该能够实现我们最初的目标。

我再稍微总结一下:

passport实现的功能就是登陆时,自动登陆各个站点;登出时,自动登出所有站点。这里和通常意义的passport不一样,因为通常意义的passport一般就只有一个cookie。

一个新插入的子站点(不管自身项目还是开源项目),若想使用passport认证,则进行如下操作:

1、找到登陆的代码,套入passport提供的模板页面中;
2、找到登陆和登出的链接,指向passport;

当然,对于第二步来说,仍然比较困难,但是较之完全替换其帐户系统,理论上讲应该轻松许多,这也是我目前能想到的代价最小的实现Single Sign On的方式了。

由于这只是一些设想,跟曾毅和卢浩森聊了一下,据称问题不大,而且曾毅貌似已在音乐站上测试成功第一阶段的目标,所以特贴出来,看看各位达人认为:

1、有可行性么?
2、有没有其他更好更贴近栋力无限自身特点的方式?

多文件批量上传

用一个页面实现文件上传,一般只需要使用HtmlInputFile的runat=server版本就行了,但是现在有些不同的地方:
1、直接上传到数据库的image类型的字段中,而不产生本地文件;
2、多个文件同时上传;
3、上传的文件组最后要同其他相关信息进行关联;

解决方法主要有两种:
1、每次上传一个文件到临时目录,返回并保存相关信息到ViewState,最后提交的时候,统一将文件写入数据库,并根据相关信息做关联;
2、每次上传一个文件到数据库,返回并保存相关信息到ViewState,最后提交的时候,根据相关信息做关联;
3、每次采集HtmlInputFile中的信息到ListBox,最后统一上传多个文件,并做相关关联;

无疑,第三种是其中最好的一种,但是上传多个文件却比较麻烦,本来想在提交的时候,分别多次设置 HtmlInputFile.PostedFile.FileName属性,再多次调用SaveAs进行上传,但在一次提交过程中它不允许重设 FileName属性(事后想想,确实应该不行:P)
后来又想在后台动态添加HtmlInputFile控件并且设其Visible属性为false,结果好像还是不行,因为还是不准自己指定那个怨念的FileName属性

结果:
增加文件按钮:
if( null == m_htmlInputFile )
m_htmlInputFile = new ArrayList();
m_htmlInputFile.Add( FileInputField );

其中FileInputField为System.Web.UI.HtmlControls.HtmlInputFile类型;
并且注意设置m_htmlInputFile这个ArrayList为static,以防每次增加文件后被刷新为null:(
这里其实就每次Add那个Html控件就行了,他们都对应相同的html控件,但是后台的对象每Add一次增加一个:)

提交按钮:
if( null == m_imageList )
m_imageList = new ArrayList();
if( null == m_imageTypeList )
m_imageTypeList = new ArrayList();
foreach( HtmlInputFile hif in m_htmlInputFile )
{
System.Drawing.Image image = System.Drawing.Image.FromStream( hif.PostedFile.InputStream );
m_imageList.Add( image );
m_imageTypeList.Add( hif.PostedFile.ContentType );
}

显示图片:
在GetImage.aspx?ImageID=*页面的Page_Load中
Response.ContentType = m_image.ImageType;
Response.BinaryWrite( m_image.Img );

注意采用BinaryWrite以及设置ContentType为image/pjpeg等

最后在保存文件到后端数据库的时候有点小不爽:
MemoryStream ms = new MemoryStream();
image.Save( ms, System.Drawing.Imaging.ImageFormat.Jpeg );//image是System.Drawing.Image
m_image = new byte[ms.Length];//m_image是byte[]
ms.Position = 0;
//If Too Long, ...
ms.Read( m_image, 0, Convert.ToInt32(ms.Length));

这里ms.Length是long类型,但是同一个类-MemoryStream的Read方法的第三个参数是int型,涉及到一个向下的类型转换,如果Length过大,应该这里会有问题,不知是怎么回事:(

发布Web Services

笔记本上Release后一切正常的Web Services,部署到另外一台2003的服务器上,出现两个问题:

1、Critical Error:

…没有权限访问注册表

其实只是在写入EventLog的时候ASPNET的权限不够,不过奇怪的是把ASPNET和匿名帐户加入Administrator组,重启,好像权限还是不够>.<

查看其列出的跟踪堆栈,发现好像是Enterprise Library中ADO.NET连接数据库的问题,在网上查了查,有用的文章不多,最后居然就在EntLib的FAQ里,发现是没有正确部署EnterLib程序集的问题。

安装程序工具(Installutil.exe)
允许通过执行指定程序集的安装程序组件来安装和卸载服务器资源。
注意: 在“%windir%Microsoft.NETFrameworkv1.1.xxxx”中可以找到Installutil.exe,其中xxxx是您使用的 .NET Framework 的内部版本号。

Installutil Microsoft.Practices.EnterpriseLibrary.Common.dll
Installutil Microsoft.Practices.EnterpriseLibrary.Configuration.dll
Installutil Microsoft.Practices.EnterpriseLibrary.Data.dll

一切恢复正常。

不过还是不知道为什么他的程序集部署的时候必须Install,而自己的dll就不用这一步。

PS:
删除用
Installutil /u xxx.dll

2、localhost访问没有问题,但用www.xxx.com访问,出错

在 .NET Framework 1.1 远程环境中,默认情况下 HTTP GET 和 HTTP POST 同时被禁用。这是出于安全方面的考虑。
当 Web 服务升级到 .NET Framework 1.1 后,应用程序使用 HTTP GET 或 HTTP POST 调用 Web 服务时会失败。
这些应用程序会收到以下一条错误信息 System.Net.WebException指出无法识别请求格式。

由于使用POST,现在已基本不可能转成SOAP协议,只有将其打开,更改Web.Config文件:
<configuration>
<system.web>
<webServices>
<protocols>
<!--
<add name="HttpGet">
-->
<add name="HttpPost"/>
</protocols>
</webServices>
</system.web>
</configuration>

Bug in Enterprise Library

TNND,最近在用Enterprise Library的时候,需要自己实现他的IAuthorizationProvider接口,然后在用他的配置工具(EntLibConfig.exe)进行配置的时候,老是提示:

The type name XXX is not valid.

但是我看了几百遍,还是觉得应该实现方式没有什么问题。好在Enterprise Library带有源码,我直接把他配置工具的下面行注释掉:

// errors.Add(instance, name, SR.ExceptionTypeNotValid(typeName));

于是一切使用正常,说明确实没有问题。但心中总是不爽。

今天在网上翻,终于发现,果然是微软的一个bug….

The problem is that some of the configuration settings have the [TypeValidation] attribute, which will attempt to check that the assembly qualified name of the type is a valid type. Unfortunately, this is running in an App Domain which is rooted in the EntLibConfig.exe directory, and so will only be able to resolves types in assemblies in that directory, or in the GAC.

而且提供的暂时解决方案居然就是直接注释掉那一行,他还友情提醒,因为没有检测,自己注意类型不要弄错了- –

唉…郁闷- –

所以,如果确实不想改动源码,可以直接把生成的DLL,用生成后事件copy到EntLibConfig.exe目录下就行了。

公车奇遇

那天晚上依然很晚下班,等了半天,倒霉685才来,已经人满为患了,我就站在中间位置。

旁边单座上坐着一个身材魁梧,肉顿顿的男子,约25岁。一个娇小女子坐在男子腿上,二人呈环抱状,两个人不停的谈话,男子嗓门极大,然后他说:”我给你猜个谜吧””好呀好呀””说巧克力和爆米花打架,巧克力赢了,打一种食物”,那女的做痴呆思考状,然后我也在那想,我估计车上很多人都在思考,后来那女的猜了几个不着边的东西都不对,我也快到站了,我就着急呀,快说答案呀,要不我得别扭死…

那男的很深沉的说,”我可说答案了呀…答案就是,巧克力棒!!!哈哈哈哈哈”…

我差点一口血吐出来,然后整个车上弥漫着那雄壮男的笑声。