(一)用户角色的问题:
匿名用户和非匿名用户虽然可以在一定程度上避免访问人数太多带来的管理紊乱和安全性问题,但是仅有这项是远远不够的。为了职责明确,避免不必要的纠葛和扯皮的事情发生,在后台网站管理上通常我们会给不同登录的人员分配不同的任务,赋予它们访问不同页面的权限。我们通常把这种称之为“用户角色”(Role)。
在ASP.NET中启用用户角色非常简单——只需解决方案管理器右上角的一个(榔头+地球)的图标,进入页面之后点击“Enabled Role”,然后添加一些角色名称(随便,比如admin,guest,user等)。启用Role在web.config对应的代码也是异常简单:
<roleManager enabled="true" cacheRolesInCookie="true">
</roleManager>
如果你要对某个页面(或者某个路径下所有页面)权限限制,只要这样做:
<location path="Default2.aspx">
<system.web>
<authorization>
<allow roles=”admin”/>
<deny roles=”*” />
</authorization>
</system.web>
</location>
这样一来,不是admin权限的人就不能访问这个页面了(会出错的,说页面找不到)。不过这一个页面太“土”,您完全可以配置自己的页面,在web.config中这样配置:
<customErrors mode="RemoteOnly" >
<error statusCode="403" redirect="NoAccess.htm" />
</ customErrors>
(二)Profile(个性文件)配置:
如果你上百度、Google等一些大型网站,您会发现即便你没有登录,你照样也可以对某些页面进行设置(比如调整背景色、大小等)。稍后你关闭页面,隔一段时间之后打开仍然是你原先设置的样子,这个效果通常被我们成为个性配置(Profile)。
以往在传统WinForm程序中,保留这种个性配置可以通过读写xml文件和(反)序列化以达到目的。但是Web程序是无状态连接,同时其机制也迫使其不可能像WinForm程序一样可以把所有配置一股脑儿地往客户端那儿抛,然后由服务器读取。在最初的一段日子里,我们经常用Session存储每一个私有用户的信息,但是Session一旦用户中断连接(无论正常与否)结果Session的东西全部被丢弃了。如果放到Cookie里呢,Cookie只能存储字符串,不能存储大量对象的东西。所以我们急迫需要一种新技术——该技术既可以以对象的模式记录每一个用户的内容,同时还要保证客户端即便中断也不丢失信息。这种技术在ASP.NET2.0中已经被实现,成为Profile。
比如一个用户访问百度的页面(假设是ASP.NET开发该页面),那么我们要记住他曾经第一次设置的背景色,以便下次访问仍旧记住该背景色(个性化)。我们可以按照以下步骤在前几章的基础上进行配置:
1、配置Profile(web.config):
<anonymousIdentification enabled="true"/>
<profile automaticSaveEnabled="true">
<properties>
<add allowAnonymous="true" defaultValue="White" />
</properties>
</profile>
这里注意几点:
“anonymousIdentification”:表示启用匿名机制。
“automaticSaveEnabled”:表示当使用Profile的地方页面(被)提交的时候,自动写入数据库对应的profile中所有东西(如果是false,您必须使用Profile.Save()强行写入数据库)。
“allowAnonymous”:表示当前的这个可被记录的属性是否支持匿名。
2、现在我们再次回到前面那个“个性化设置背景色”问题——假设你的页面是这样设计的(加粗体字是因为原来生成的页面代码中不包含此类相关信息,为了便于后台控制):
<body id="mybody" runat="server">
……
先请您在Page_Load中代码这样写:
protected void Page_Load(object sender, EventArgs e)
{
mybody.Style[HtmlTextWriterStyle.BackgroundColor] = Profile.BackColor; //动态设置BackColor的内容,注意到Profile.的时候,智能感知出来BackColor的提示了吗?可见Profile是强类型的。
}
随后放上一个按钮,然后在按钮中简单写这样的代码:
protected void Button1_Click(object sender, EventArgs e)
{
Profile.BackColor = “Blue”;
}
点击按钮,然后关闭浏览器,然后打开页面,是不是背景色发生变化了?很有意思吧?
(三)Profile多类型嵌套:
有时为了比较系统化、正规化描述一个人的特征(比如身高、年龄。性别等)。我们往往可以这样做:
<profile automaticSaveEnabled="true">
<properties>
<group>
<add/>
<add/>
<add/>
</group>
</properties>
</profile>
然后我可以这样调用代码:Profile.Person.Sex/Age/name….
Group标签就是用于嵌套属性的,原则上可以嵌套N个属性,而且可以为每个属性指定是否匿名等特性。同时,大家应该注意到一个问题,Age和Sex的类型不应该是String的(因为默认是String)类型,所以我在此地强制指定了它们的类型。现在带来一个新问题——如果某个Profile的属性是自定义类型呢?
(四)Profile的自定义类型:
在Profile中,自定义变得非常直观而且简单:
<profile automaticSaveEnabled="true">
<properties>
<add type='InfoList' serializeAs='Binary'/>
</properties>
</profile>
注意:type这里的完整写法是“命名空间.类名”,但是由于InfoList没有命名空间(直接在App_Code文件夹下),所以允许这样写;同时必须把这个类表示成[Serializable],以便序列化后写入数据库(Binary的形式)。
(五)匿名用户和登录用户的Profile:
现在无论是淘宝、网易,还是百度的商品拍卖网站上都有“购物车”,其大致可以允许你暂时在未登录的情况下先选择物品添加进入购物车,然后结账的时候登录,成功以后把所有匿名选择的物品转移到已登陆的那个帐号。这功能很容易实现吗?我们先看一个小例子(方便起见,这里只在Person下弄一个Cart的字符串数组):
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ProfileManager.DeleteInactiveProfiles(ProfileAuthenticationOption.All, DateTime.Now);
Profile.Person.Cart = "苹果";
Profile.Save();
Response.Write("用户名?:" + Profile.UserName);
Response.Write("购物情况?:" + Profile.Person.Cart);
}
}
protected void Button1_Click(object sender, EventArgs e)
{
FormsAuthentication.SetAuthCookie("abd", true);
Response.Write("用户名:" + Profile.UserName);
Response.Write("购物情况:" + Profile.Person.Cart);
}
或许我们以为:点击Click提交以后自然会把abc赋值给UserName属性,并且一同把数值带来了,但是实际并非我们想象那么简单:结果你会发现“用户名”虽然是abc,但是Cart却空了(丢失数据了?)
这里的原因非常简单:因为Profile在匿名存储数据的时候,会发送一个随机的Cookie给客户端(Id是唯一的),然后写入数值。但是你登录以后,原先的凭证就被作废,自动换一个新的凭证了。所以解决方案就是在SetAuthCookie的时候必须获取原来那个即将丢失的凭据,然后赋值给当前的Profile。
这里提供一个解决方案(在Global.asax加入以下代码):
void Profile_MigrateAnonymous(Object s,
ProfileMigrateEventArgs e)
{
ProfileCommon anonProfile = Profile.GetProfile(e.AnonymousID); //通过该方法将获得匿名用户的Profile
Profile.Person.Cart = anonProfile.Person.Cart; //重新赋值给当前的Profile
}
(六)Profile的清理工作
你的网站曾经被大量的“匿名”和“登录”用户访问,结果造成你的aspnet_profile数据库中挤满了一堆不需要的垃圾数据。您可以清理。清理的方法当然也是直接使用ProfileManager.DeleteInactiveProfiles,其具体参数如下:
ProfileManager.DeleteInactiveProfiles(ProfileAuthenticationOption.All, DateTime.Now.AddDays(-7));
其中“ProfileAuthenticationOption”是一个枚举,指示删除的Profile的范围(全部All,登录用户Authenticated和匿名用户Anoymous)。而DateTime是删除在此日期前(包括此日期自身)的Profile,如上面的代码所示:删除距离现在7天前的所有用户的Profile。