因此要实现单点登录,必须做两件事:
1、把bbs和www两个网站关于身份认证的Cookie信息写到一个Cookie中;
2、设置这个Cookie的Domain属性,让IE在发送Request时无论访问上述哪个网站,都会把这个Cookie发送过去。
此时,无论浏览器访问哪个网站,都会把相同的Cookie发送给服务器,服务器就可以根据这个Cookie判断客户端的身份了。
首先在
www.baibupai.com这个网站的web.config中,我配置了一个节点:
<appSettings>
<add key="domainName" value=".baibupai.com"/>
</appSettings>
在DiscuzNT的general.config中设置:
<CookieDomain>.baibupai.com</CookieDomain>
把DiscuzNT的dll文件拷贝的网站的bin目录,把Discuz的config文件夹和DNT.config文件也拷贝到www网站的根目录下。
把DiscuzNT的dll Import到工程中来
修改网站的登录代码:
private static void SetCookie(HttpCookie cookie, string domainName, bool IsPersistent, DateTime expires)
{
if (cookie != null)
{
cookie.Domain = domainName;
if (IsPersistent) cookie.Expires = expires;
HttpContext.Current.Response.Cookies.Add(cookie);
}
}
public static void Login(string name, string password, bool IsPersistent)
{
if (Membership.ValidateUser(name, password))
{
FormsAuthentication.SetAuthCookie(name, IsPersistent);
HttpCookie cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
string domainName = ConfigurationManager.AppSettings["domainName"];
DateTime expires;
if (IsPersistent) expires = DateTime.Now.AddMinutes(525600);//一年
else expires = DateTime.Now;
SetCookie(cookie, domainName, IsPersistent, expires);
//以上是常规的Membership的登录代码
//以下是Discuz的登录代码
//删除当前IP的历史登录错误
LoginLogs.DeleteLoginLog(DNTRequest.GetIP());
int uid = new MemberFcd().BbsGetUidByName(name);
//根据积分重新刷新用户的积分用户组(如果不是积分用户组用户则不受影响)
UserCredits.UpdateUserCredits(uid);
//更新用户动作,olid当前用户在在线列表中的ID
Discuz.Config.GeneralConfigInfo config = Discuz.Config.GeneralConfigs.GetConfig();
Discuz.Entity.OnlineUserInfo oluserinfo = OnlineUsers.UpdateInfo(config.Passwordkey, config.Onlinetimeout);
OnlineUsers.UpdateAction(oluserinfo.Olid, UserAction.Login.ActionID, 0, config.Onlinetimeout);
//更新用户最后登录时间
Users.UpdateUserLastvisit(uid, DNTRequest.GetIP());
//写入用户的论坛登录cookie,由于在general.config中设置了CookieDomain,所以此Cookie的Domain也是.baibupai.com
if (IsPersistent) ForumUtils.WriteUserCookie(uid, 525600, config.Passwordkey, 1, DNTRequest.GetInt("loginmode", -1));
else ForumUtils.WriteUserCookie(uid, Utils.StrToInt(DNTRequest.GetString("expires"), -1), config.Passwordkey, 1, DNTRequest.GetInt("loginmode", -1));
cookie = HttpContext.Current.Request.Cookies["dnt"];
cookie = HttpContext.Current.Request.Cookies["dnttemplateid"];
//bbs end
}
}
3、退出出逻辑
同理当用户在网站上退出时,必须同时清除Asp.net的Cookie和Dnt的Cookie,在我的代码中,我设置所有相关Cookie变量的过期时间为DateTime.Now.AddDays(-1),这样浏览器会自动清除这些Cookie变量。
网站的Logout代码:
Session.Clear();
ForumUtils.ClearUserCookie();
Utils.WriteCookie("dnttemplateid", "", -999999);
Users.UpdateOnlineTime(Utils.StrToInt(ForumUtils.GetCookie("userid"), -1));
Discuz.Config.GeneralConfigInfo config = Discuz.Config.GeneralConfigs.GetConfig();
Discuz.Entity.OnlineUserInfo oluserinfo = Discuz.Forum.OnlineUsers.UpdateInfo(config.Passwordkey, config.Onlinetimeout);
OnlineUsers.DeleteRows(oluserinfo.Olid);
string domainName = ConfigurationManager.AppSettings["domainName"];
System.Web.HttpCookie c = Request.Cookies[FormsAuthentication.FormsCookieName];
if (c != null)
{
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(c);
}
c = Request.Cookies["dnt"];
if (c != null)
{
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(c);
}
c = Request.Cookies["dnttemplateid"];
if (c != null)
{
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(c);
}
同时必须修改DiscuzNT的Logout.aspx页面的代码,使其能够执行退出www网站的操作。
这里必须使用反编译工具,反编译得到Logout.aspx.cs源文件。
在:Utils.WriteCookie("dnttemplateid", "", -999999);之后插入:
string domainName = config.CookieDomain;
System.Web.HttpCookie c = System.Web.HttpContext.Current.Request.Cookies["baibupai"];
if (c != null)
{
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
System.Web.HttpContext.Current.Response.Cookies.Add(c);
}
System.Web.HttpContext.Current.Response.Cookies.Add(c);
c = System.Web.HttpContext.Current.Request.Cookies["dnt"];
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
System.Web.HttpContext.Current.Response.Cookies.Add(c);
c = System.Web.HttpContext.Current.Request.Cookies["dnttemplateid"];
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
System.Web.HttpContext.Current.Response.Cookies.Add(c);
由于DiscuzNT的聚合页面:website.aspx的退出没有经过Logout.aspx的处理,必须对其退出进行特殊的处理:
在
Utils.WriteCookie("dnttemplateid", "", -999999);
和
base.SetUrl("website.aspx");
之间插入:
string domainName = config.CookieDomain;
System.Web.HttpCookie c = System.Web.HttpContext.Current.Request.Cookies["baibupai"];
if (c != null)
{
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
System.Web.HttpContext.Current.Response.Cookies.Add(c);
}
System.Web.HttpContext.Current.Response.Cookies.Add(c);
c = System.Web.HttpContext.Current.Request.Cookies["dnt"];
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
System.Web.HttpContext.Current.Response.Cookies.Add(c);
c = System.Web.HttpContext.Current.Request.Cookies["dnttemplateid"];
c.Domain = domainName;
c.Expires = DateTime.Now.AddDays(-1);
System.Web.HttpContext.Current.Response.Cookies.Add(c);
此时,单点登录的主要工作已经完成了,在
www.baibupai.com的login.aspx页面登录后,再访问bbs.baibupai.com,会显示自己已经登录。
同时,无论在哪个网站选择退出,都会同时退出另一个网站。
但还有两个缺点:
1、在DiscuzNT的分栏模式下,没法实现框架页面的登录状态与前台网站同步。
2、访问前台网站的用户,不会出现在论坛的在线会员中。
造成问题1的原因是,登录后,frame内的页面登录状态已经正确,但外面的frame页面没有经过刷新,仍然保持登录前的状态。
因此只要保证内页登录后,外部页面进行刷新即可。
首先要在discuzNT的页面中增加一个静态页面:refresh.htm。
页面中只须一句Javascript
<script language="javascript" type="text/javascript">window.location='focuslist.aspx';window.top.leftmenu.location='forumlist.aspx';</script>
由于是htm页面,所以必须放在DiscuzNT的根目录下,不要放在aspx目录下。
discuzNT在分栏模式下,会在Cookie中写一个"refurl"变量,记录是从哪个页面转过来的。这样在只要判断
cookie中有这个数据,即可判断登录请求来自分栏模式下的页面。
因此需要在前台登录页面中加入如下代码:
//正常情况下的登录重定向
if (Request.QueryString["ReturnUrl"] != null && Request.QueryString["ReturnUrl"] != "")
{
string rntUrl = Request.QueryString["ReturnUrl"];
int i = rntUrl.IndexOf("?ReturnUrl");
if (i > 0) rntUrl = rntUrl.Remove(i, rntUrl.Length - i);
Response.Redirect(rntUrl);
//为了能跨站点登录,不能使用FormsAuthentication.RedirectFromLoginPage(name, false);
}
//在分栏模式下的登录重定向
else if (Request.Cookies["refurl"] != null && Request.Cookies["refurl"].Value != "")
{
if (Request.Form["hdFrame"] == "1")
Response.Redirect("
http://bbs" + ConfigurationManager.AppSettings["domainName"] + "/refresh.htm");
else Response.Redirect(Request.Cookies["refurl"].Value);
Request.Cookies.Remove("refurl");
}
这样登录后,内页会重定向到refresh.htm,执行其中的javascript,外部的框架页也就得以刷新了。
问题2:需要在www网站页面的基类中或者每个页面中都会调用的部分代码中增加如下几行:
Discuz.Config.GeneralConfigInfo config = Discuz.Config.GeneralConfigs.GetConfig();
Discuz.Entity.OnlineUserInfo oluserinfo = Discuz.Forum.OnlineUsers.UpdateInfo(config.Passwordkey, config.Onlinetimeout);
Discuz.Forum.OnlineUsers.UpdateAction(oluserinfo.Olid, Discuz.Forum.UserAction.ShowForum.ActionID, 0, config.Onlinetimeout);
这样每个访问到这段代码的用户都会出现在DiscuzNT的在线列表中。