问题背景

因为越来越烦于单位里处理办公事务的电脑开机访问站点时弹出的域帐号登录对话框,于是就寻思着想个办法解决一下。单位为了处理这个问题已经为每人提供了叫USB Key的东西,插入U盘接口后,再访问需要身份认证信息的网站时就可以自动获取这些信息而不用手工输入了。但不幸的是我的这个工具自打发下来后就以没有好用过,幸好还可以像以前一样用域帐号登录,只是每次第一次打开需要身份认证的网站都要手动录入。

无意间知道了微软很早以前就在Windows系统中集成了一个命令行工具Powershell,上网找了些参考资料一看,虽然使用者大都是系统管理员、数据库管理员,但看到它能操作.NET对象,能用在自动化部署和测试上,对开发者也是好处多多,功能甚是强大。于是就萌生了用它处理域认证的问题的想法。

解决思路

单位里决大多数B/S应用都在.NET环境下开发部署运行的,并且这些应用都使用了统一的第三方集中式身份认证接口。这个API使用了Form认证和Windows集成身份认证两种方式,关于这两种认证方式的介绍请参考这篇文章:http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.htmlhttp://www.cnblogs.com/fish-li/archive/2012/05/07/2486840.html

按照这个思路,首先要知道这个身份认证API的认证机制。因为按照一般操作,如果是第一次登录网站,客户端上可能找不到身份认证信息,于是如果选择的是Windows集成身份认证方式,它会弹出一个输入认证信息的Windows对话框。等用户录入提交后,就发送给身份认证服务器进行验证。如果验证成果,它会以某种方式(Cookies)把这些信息存储在客户端。这样在下次访问或访问其它使用相同身份验证API的应用时,就可以直接使用而不用重复登录了。另外如果在Internet上访问域中的服务器,解决方案请参考这篇文章:http://www.pstips.net/access-web-app-that-authenticated-with-adfs.html

于是解决登录问题时,只要发给身份验证API一个请求,并在请求头中加上它需要的认证信息,再解析响应中的认证并把身份信息存储在客户端。理论上用Powershell调用.NET对象完成这些工作是可行的,但问题是这个过程应该怎么实现,要说清楚整个过程貌似可以写个连载了。

于是就有了第二个方法:访问一个需要认证的站点,弹出Windows域认证对话框后,自动用Powershell填写用户名/密码,再自动点击确认,模拟用户操作来完成这个任务。

用Powershell自动实现填写Windows对话框和确认动作

有了以上想法就可以写Powershell脚本实现了。基本的流程就是启动一个IE窗口,导航到一个需要认证的站点,在它弹出的对话框中自动填写用户名和密码,再点击确定就OK了。

启动IE并导航到某站点是很容易的,就是调用一个COM对象。代码如下:

$IEHost = New-Object -ComObject "InternetExplorer.Application"
$IEHost.Navigate(yourUrl)
$IEHost.Visible = $true

这样就会弹出一个干净的IE窗口并导航到指定网址。

但涉及到自动化操作就有些麻烦了。要自已写方法吗,想想涉及到进程、WinForm编程之类的,好像也是可以写个连载的。按照少写代码的原则,还是找个现成的库吧。翻阅现在的Powershell资料,很少有涉及到这种模拟WinForm应用操作的,但偶然间发现了一个(可能也是唯一一个)关于这方面的库--WASP。这是一个很久没更新的库了,托管在CodePlex上。官方没有文档(其实它只有几个方法)但有一些讨论内容可以参考。

有了这个库就好多了。由于是第三方库,用之前要导入它。

Import-Module Your_Path\WASP.dll

接下来我把模拟登录操作的命令先写出来(注意,这其实是一个模拟点击弹出框的命令,但原理是一样的),很短只有一句话,再详细说明一下。

Select-Window -Class "Alternate Modal Top Most" | Select-ChildWindow | Select-Control | Send-Click -ControlButton

Select-Window代表选择窗口,即IE窗,如果你看Select-Window命令的可选项,其实除了Class,还有Title和ProcessName可用。但Title重复率高,并且有时Title是根据文档内容变化的,Process Name也不具有唯一性,个人感觉还是Class辨识度高。这里值得注意的问题是如何选择正确的类名。有一个小技巧,就是先单独使用Select-Window命令,这样它会列出所有桌面上的应用对应的类名,选择需要的即可。

之后的“|”符号代表管道操作。Select-ChildWindow表示选择这个弹出框,不直接用Select-Control是因为这个模态对话框属于窗口而不是IE的控件。

选好弹出框后就可以操作里面的控件了。Select-Control就表示选择了窗口中所有控件。因为整个窗口只有一个按钮可点击,所以就不用指定操作哪个控件了。当然有兴趣的同学也可以了解一下这个命令的参数,它也有几个参数可选来指定某些或某一个控件。

接下来Send-Click表示点击了弹出框中的控件,ControlButton表示点击了确认按钮。

这是模拟弹出框,要是有文本录入那种输入框怎么办呢?就需要这义到文本框控件,并使用Send-Keys命令就可以了,具体就是再后面加上命令:

Send-Keys -Keys "userName"

然后再Send-Click。这样就实现了一个完整的登陆操作。在同一个会话中再打开其他需要认证的站点就不需要再登陆了。

总结

上面实际上是一个迂回的方案。伟光正的做法就是解决思路的第一个思路。这里重点在于“自动”二字,这是彻底的自动--连文本录入和点鼠标的动作省去了,个人认为在一些情景下还是有用的--比如这个每次开机或清除了认证信息就提示登陆的烦人的域认证对话框。


AfternoonLeaf
804 声望101 粉丝

现从事.NET Web开发工作,专注于各项.NET技术、工作流技术和前端技术,并对各种软件应用的设计和实现技术具有浓厚兴趣。


引用和评论

0 条评论