今天遇到了一个问题. 公司有两个产品需要整合, 需要实现一个SSO... 后台做完了, 到前台这里发现了个巨大的问题:
因为这两套系统是在两个域名, 系统A在完成验证后, 用户在A系统页面关闭前, 超时范围内打开B系统都不需要再次登录.
但是, B系统凭什么认为一个新发起的请求(HttpRequest)是已经验证过身份的呢? 众所周知, Cookie不能跨域. 两系统共用Cookie的方式难度较大.安全性也有问题.
头疼, 开论坛. 手贱一般的点击了 "查看网页源代码"... 这个网页的源码中有一句话让我眼前一亮:
<script language="javacript" src="http://ad.baidu.com/......."></script>...
对阿, 对script脚本的引用是可以跨域的啊!! 恩, 好办法. 写个测试试一下. 下图为设计思路:
大体思路为: 首先在B系统的那个需要A系统数据的那个页面, 添加一个脚本引用. 注意, 这里的脚本引用不指向一个真实的JS文件, 而是指向A系统的一个WebHandler或者ASHX.
这样在打开B系统的页面时, 就会调用A系统的那个WebHandler(或ASHX), 而这个WebHandler(或ASHX)因为属于A系统, 所以可以调用A系统中当前请求的Session或者Cookie.
然后以脚本输出的方式输出成几个带有来自A系统数据的Javascript字段. 然后B系统的页面就可以通过脚本访问到字段的值. 也就是说这个WebHandler(ASHX)就充当了桥梁.
至于之后是直接显示在页面上啊还是提交到服务器就是后话了..
不明白啊? 举个例子: B公司需要A公司提供一些文本资料, 可是B公司的员工无权直接去A公司拿取资料, 因为A公司的人不认识他. 那么有一个办法, 就是B公司请求A公司派一个 "大使" 将资料送来.
那么这个 "大使" 既可以从A公司拿取资料, 因为他本身就是A公司的人, 也可以将资料交付给B公司. 因为身在B公司的办公室.
好, 首先建立两个Web工程, 分别部署在两个域名下(不过我这儿只有localhost, 就用两个端口分开). 一个是信息提供方, 一个是数据接受方. 这个是信息提供方的主页面.
因为是测试, 所以页面什么都没有. 我们在默认页中在Cookie中添加一些东西... (Session也行. 如果想要做A系统成功登陆关闭页面后超时前B系统自动登陆的话, 就用cookie)
01.
public
partial
class
_Default : System.Web.UI.Page
02.
{
03.
protected
void
Page_Load(
object
sender, EventArgs e)
04.
{
05.
Random r=
new
Random(10000);
06.
Int32 i = r.Next();
07.
i += (((DateTime.Now.Year + DateTime.Now.Month + DateTime.Now.Day + DateTime.Now.Hour + DateTime.Now.Minute + DateTime.Now.Second + DateTime.Now.Millisecond) / 2) * i);
08.
09.
10.
HttpCookie c=
new
HttpCookie(
"TEA"
, i.ToString());
11.
c.Expires = DateTime.Now + TimeSpan.FromMinutes(50);
12.
13.
14.
Response.SetCookie(c);
15.
16.
17.
Response.Write(i);
18.
19.
}
20.
}
下面这个就是用于处理从B系统页面发出的“伪JS”请求的HttpHandler了.
01.
public
class
JSRequetHandler:IHttpHandler
02.
{
03.
#region IHttpHandler 成员
04.
05.
public
bool
IsReusable
06.
{
07.
get
{
return
true
; }
08.
}
09.
10.
public
void
ProcessRequest(HttpContext context)
11.
{
12.
if
(context.Request.Cookies[
"TEA"
] ==
null
)
13.
{
14.
context.Response.Write(
"var login_status = 0"
);
15.
}
16.
else
17.
{
18.
context.Response.Write(
"var login_status = '"
+ context.Request.Cookies[
"TEA"
].Value +
"';"
);
19.
20.
}
21.
}
22.
23.
#endregion
24.
}
因为我这里用的是HttpHandler, 所以配置文件中还得写点东西. 如果是ASHX的话就不用写了.
01.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02.
<
configuration
>
03.
04.
<
system.web
>
05.
06.
<
httpHandlers
>
07.
<
add
verb
=
"*"
path
=
"A.cj"
validate
=
"false"
type
=
"Handler.JSRequetHandler, Handler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
/>
08.
09.
10.
</
httpHandlers
>
11.
</
system.webServer
>
12.
</
configuration
>
调用页
01.
<%@ Page Language="C#" AutoEventWireup="true" %>
02.
04.
06.
<
head
runat
=
"server"
>
07.
<
title
>B</
title
>
08.
<
script
language
=
"javascript"
type
=
"text/javascript"
src
=
"http://201.86.189.40:5678/TestWebSite01/A.cj"
></
script
>
09.
<
script
runat
=
"server"
>
10.
public void OnButtonServerClick(Object sender, EventArgs arg)
11.
{
12.
String fromA = mk.Value;
13.
//A系统的数经过千辛万苦终于抵达B系统的后台.
14.
}
15.
</
script
>
16.
</
head
>
17.
<
body
>
18.
<
br
/>
19.
<
br
/>
20.
<
br
/>
21.
<
br
/>
22.
<
center
style
=
"font-size:60pt; font-family:黑体;"
id
=
"t"
></
center
>
23.
<
br
/>
24.
<
br
/>
25.
26.
<
form
id
=
"f"
runat
=
"server"
style
=
"text-align:center;"
>
27.
<
input
type
=
"hidden"
id
=
"mk"
runat
=
"server"
/>
28.
<
button
id
=
"btn"
runat
=
"server"
onserverclick
=
"OnButtonServerClick"
>提交到服务器</
button
>
29.
</
form
>
30.
31.
<
script
language
=
"javascript"
type
=
"text/javascript"
>
32.
33.
var _t = document.getElementById("t");
34.
35.
if(typeof(login_status) != 'undefined')
36.
{
37.
//A系统由JSRequestHandler输出的脚本已经完成输出, 数据已经保存在字段里了.
38.
_t.innerHTML = login_status;
39.
document.getElementById("mk").value = login_status;
40.
41.
}
42.
else
43.
{
44.
_t.innerHTML="-";
45.
//如果未定义, 那么表明A系统并未提供数据.
46.
}
47.
</
script
>
48.
49.
</
body
>
50.
</
html
>
测试结果: 先打开A系统(就是从上数第一个Default页所在的那个Web工程) 启动后页面会输出一串数字, 然后启动B系统(调用页所在的Web工程), 所看到的数字是一致的. 测试成功.
Ps:据说W3C已经出了CrossDomain的规范了? 不过无论怎样, IE6还是不支持这个规范的... 毕竟中国还有好多人还在用IE6...