Security Tutorials系列文章第七章:User-Based Authorization(下)
首先打开Membership文件夹里的UserBasedAuthorization.aspx页面,添加一个名为FilesGrid的GridView控件.在智能标签里点“Edit Columns”以打开Fields对话框,在对话框里不要点“Auto-generate fields” checkbox,然后,添加一个Select按钮、一个Delete按钮以及2个BoundFields(Select 和 Delete按钮位于CommandField下面)。设Select按钮的SelectText属性为“View”,在将第一个BoundField的HeaderText 和 DataField属性设为“Name”.将第二个BoundField的HeaderText设置为“Size in Bytes”,而将DataField属性设为“Length”,此外DataFormatString属性为“{0:N0}” ,HtmlEncode属性为False.
设置完后点OK关闭Fields对话框.在属性窗口里将GridView的DataKeyNames属性设置为FullName。这样,该GridView的声明代码就和下面的差不多:
<asp:GridView ID="FilesGrid" DataKeyNames="FullName" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:CommandField SelectText="View" ShowSelectButton="True"/>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="Name" HeaderText="Name" />
<asp:BoundField DataField="Length" DataFormatString="{0:N0}" HeaderText="Size in Bytes" HtmlEncode="False" />
</Columns>
</asp:GridView>
接下来,我们要写代码在某个文件夹里检索文件并绑定到GridView,在页面的Page_Load的事件处理器里添加如下的代码:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string appPath = Request.PhysicalApplicationPath;
DirectoryInfo dirInfo = new DirectoryInfo(appPath);
FileInfo[] files = dirInfo.GetFiles();
FilesGrid.DataSource = files;
FilesGrid.DataBind();
}
}
上述代码使用DirectoryInfo class类来获取应用程序根目录下的文件列表.而GetFiles()方法将所有的文件以FileInfo objects的形式返回数组,然后绑定到GridView,该FileInfo object有诸如Name, Length, 以及IsReadOnly等的属性.如代码所示,GridView仅仅显示了其Name 和 Length属性.
注意:DirectoryInfo 和 FileInfo类属于System.IO命名空间.因此你在使用这些类时要么加命名空间前缀,要么将命名空间导入类文件(比如:using System.IO)
花点时间来测试页面,它将会将根目录下的文件显示出来,点“View” 或 “Delete”按钮都会产生页面回传,但不会真的发生什么动作,因为我们还没有创建相关的事件处理器.
图7:
我们需要将选中文件的内容显示出来.重新打开Visual Studio,在GridView上添加一个名为FileContents的TextBox.设其TextMode属性为MultiLine,Width和Rows属性分别为“95%”和10.
<asp:TextBox ID="FileContents" runat="server" Rows="10" TextMode="MultiLine" Width="95%">
</asp:TextBox>
接下来为GridView的SelectedIndexChanged事件添加如下的代码:
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
// Open the file and display it
string fullFileName = FilesGrid.SelectedValue.ToString();
string contents = File.ReadAllText(fullFileName);
FileContents.Text = contents;
}
代码使用GridView的SelectedValue属性来判断所选文件的完整文件名.在内部,通过引用DataKeys来获得SelectedValue,因此我们务必要将GridView的 DataKeyNames属性设置为“Name”.我们使用File class类来将所选文件的内容读入一个字符串,再赋值给FileContents TextBox的Text属性,这样就将所选文件的内容显示在页面上了.
图8
注意:
如果你查看的内容包含HTML标记,然后尝试查看或删除一个文件话,你将收到一个HttpRequestValidationException错误.因为在回传时TextBox的内容将发回到web server,默认情况下,任何时候,当回传的内容存在潜在危险时,比如包含HTML标识,当检测到时就会引发一个HttpRequestValidationException错误.为避免这个报错,我们在页面的@Page声明里添加ValidateRequest="false".关于request validation的好处以及如何禁用它的更多详情请参阅文章《Request Validation – Preventing Script Attacks》
最后为GridView的RowDeleting事件添加如下的代码:
protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
string fullFileName = FilesGrid.DataKeys[e.RowIndex].Value.ToString();
FileContents.Text = string.Format("You have opted to delete {0}.", fullFileName);
// To actually delete the file, uncomment the following line
// File.Delete(fullFileName);
}
代码仅仅将要删除的文件的完整文件名显示在FileContents TextBox里,并没有真正的删除文件
图9
在第一步里我们禁止匿名用户访问Membership文件夹里的文件,为了更好的进行演示,我们允许所有的匿名用户访问UserBasedAuthorization.aspx页面,只是匿名用户访问时页面提供的功能有限.为了使该页面对所有的用户开放,在Membership文件夹里的Web.config文件里添加 <location>元素:
<location path="UserBasedAuthorization.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
添加<location>元素完毕后,我们注销后匿名登录,就可以访问UserBasedAuthorization.aspx页面了.
当前,任何的匿名和认证的用户都可以访问UserBasedAuthorization.aspx页面,并查看和删除文件。让我们使认证用户才可以查看内容,并只有Tito可以删除文件.要达到这样的目的,我们可以通过显式声明的方式或编程的方式,或2种方法结合的方式来达到目的.我们使用显式声明的方式限制用户对文件内容的查看,而使用编程的方式限制对文件的删除.
Using the LoginView Control
正如我们在前面的文章里看到的那样,当需要分别向匿名和认证用户显示不同的内容时LoginView控件是很有用的,并可以对匿名用户屏蔽掉某些功能.因为匿名用户无法看到或删除文件,因此我们只需要在认证用户访问时显示FileContents TextBox.为此,在页面里添加一个LoginView控件,id为LoginViewForFileContentsTextBox,再将FileContents TextBox的声明代码剪切到LoginView控件的LoggedInTemplate里.
<asp:LoginView ID=" LoginViewForFileContentsTextBox " runat="server">
<LoggedInTemplate>
<p>
<asp:TextBox ID="FileContents" runat="server" Rows="10" TextMode="MultiLine" Width="95%"></asp:TextBox>
</p>
</LoggedInTemplate>
</asp:LoginView>
LoginView的templates里的Web控件不能直接在后台代码里进行引用。比如,id为FilesGrid的GridView的SelectedIndexChanged 和 RowDeleting事件处理器直接引用FileContents TextBox,如下:
FileContents.Text = text;
这样做是无效的.因为放置到LoggedInTemplate里的FileContents TextBox是不能直接引用的,我们必须通过FindControl("controlId")方法来编程引用,对上面2个事件处理器进行更新,来这样进行引用:
TextBox FileContentsTextBox = LoginViewForFileContentsTextBox.FindControl("FileContents") as TextBox;
FileContentsTextBox.Text = text;
完成后,匿名访问该页面.如图10所示,FileContents TextBox就没有显示出来了,而“View” LinkButton依然可见.
图10
对匿名用户隐藏“View”按钮的一种方法是将其转换为一个TemplateField,这样将生成一个包含“View” LinkButton声明代码的模板.我们然后在模板里添加一个LoginView控件并将一个LinkButton放置在LoginView的LoggedInTemplate里,这样就达到了对匿名用户隐藏“View” 按钮的目的.为此,在GridView的智能标签里点“Edit Columns”打开Fields对话框,然后选中Select按钮,再点击“Convert this field to a
TemplateField” ,这样将使该字段的声明代码由<asp:CommandField SelectText="View" ShowSelectButton="True"/>改成
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False" CommandName="Select" Text="View">
</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
这时,我们可以向TemplateField添加一个LoginView,如下的代码使“View” LinkButton只对认证用户可见:
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LoginView ID="LoginView1" runat="server">
<LoggedInTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
CommandName="Select" Text="View">
</asp:LinkButton>
</LoggedInTemplate>
</asp:LoginView>
</ItemTemplate>
</asp:TemplateField>
如图11所示,最终结果还不太完美,因为虽然“View” LinkButtons隐藏了但“View”这一列还是显示出来了.我们将在下一节看如何将这一列完全隐藏起来(而不仅是将LinkButton隐藏起来)
图11
Programmatically Limiting Functionality
在某些情况下,仅仅通过显式声明的方式来限制页面功能还是不够的.比如有些功能时否可用不是仅仅通过判断用户是匿名还是认证用户那么简单.在这种情况下,就要通过编程的方式来显示或隐藏各种用户界面了.为了通过编程的方式限制页面功能,我们要解决2个问题:
1.判断访问页面的用户是否可以使用某个功能
2.根据用户是否有权使用某功能来改变用户界面
为了对上述2个问题进行探讨,我们只允许Tito从GridView删除文件.第一个问题是判断当前用户是否是Tito,有了判断结果后再决定隐藏(或显示)GridView的Delete列.GridView的列可以通过其Columns属性来访问,只有当其Visible属性为true(默认值)时该列才会显示出来.
在将数据绑定到GridView前,先在Page_Load事件处理器里添加如下的代码:
// Is this Tito visiting the page?
string userName = User.Identity.Name;
if (string.Compare(userName, "Tito", true) == 0)
// This is Tito, SHOW the Delete column
FilesGrid.Columns[1].Visible = true;
else
// This is NOT Tito, HIDE the Delete column
FilesGrid.Columns[1].Visible = false;
如我们在前面的文章《An Overview of Forms Authentication》里探讨的那样,User.Identity.Name返回的时用户身份的名称.如果是Tito访问页面那么GridView的第二列的Visible属性就为true;否则就设置为false.最终结果是只要访问者不是Tito,比如匿名用户或其他认证用户,Delete列就不会显示出来(如图12);然而,如果是Tito访问则将Delete列显示出来(如图13).
图12
图13
第四步:Applying Authorization Rules to Classes and Methods
在第三步里我们禁止匿名用户访问一个文件的内容,同时只允许Tito删除文件.为此,我们同时使用了显式声明和编程2种技术来对未被授权的用户隐藏相应的用户界面.对本例而言,可能隐藏用户界面是直观易懂的,但对其他更复杂的站点而言又如何呢?它们可能有多种方式来执行相同的功能。对未授权的用户限制页面功能时,如果我们忘记隐藏或禁用所有的用户界面元素呢?
确保未授权的用户不能使用某个功能的一个简单办法是对该类或方法用PrincipalPermission特性进行修饰.当.NET runtime使用一个类或执行其中的一个方法时,它要检查以确保当前安全内容(current security context)有权使用某类或执行某个方法.PrincipalPermission提供了一种机制,通过该机制我们就可以定义规则.
让我们做个演示,对GridView的SelectedIndexChanged 和 RowDeleting事件处理器分别运用PrincipalPermission特性,分别禁止匿名用户和除Tito外的认证用户使用.我们要做的仅仅是分别在相应的函数定义上添加恰当的特性:
[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{ ... }
[PrincipalPermission(SecurityAction.Demand, Name="Tito")]
protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{ ... }
为SelectedIndexChanged事件处理器添加的特性指出只有认证用户才可以执行该事件处理器;而RowDeleting事件处理器上添加的特性指出只有Tito才可以执行.
如果一个不是Tito的认证用户想执行RowDeleting事件处理器或一个匿名用户想执行SelectedIndexChanged事件处理器的话,.NET runtime将触发一个SecurityException异常.
图14
注意:
要允许多个security contexts访问一个类或方法,每个security contexts要用PrincipalPermission特性对类或方法进行修饰.打个比方,要允许Tito 和 Bruce执行RowDeleting事件处理器,要添加2个PrincipalPermission特性:
[PrincipalPermission(SecurityAction.Demand, Name="Tito")]
[PrincipalPermission(SecurityAction.Demand, Name="Bruce")]
除了ASP.NET页面外,很多应用程序都包括很多层,比如Business Logic 和Data Access层,这些层典型的都以类库(Class Libraries)的形式执行,包含了很多的类和方法来执行业务逻辑和数据相关的功能.要对这些层应用授权规则的话,PrincipalPermission特性是很有用的.运用PrincipalPermission特性对类和方法定义授权规则的更多详情,请参阅Scott Guthrie的博客《Adding Authorization Rules to Business and Data Layers Using PrincipalPermissionAttributes.》
结语:
本文我们考察如何应用基于用户的授权规则.我们首先考察了ASP.NET的URL authorization framework.对每个请求,ASP.NET引擎的UrlAuthorizationModule都会检查定义在配置文件里的URL authorization规则,以判断当前用户是否有权访问请求的资源.简单的说,URL authorization可以很方便地对某个页面或某个文件夹里的所有页面定义授权规则.
URL authorization framework是基于page-by-page的原则施加授权规则的.利用URL authorization,用户要么允许要么禁止访问某个具体的资源.很多情况下,我还需要更精细的授权规则,而不仅仅是判断哪些可以访问哪些不能访问某个页面.有时我们允许所有人访问页面,但是对不同的用户显示不同的数据和提供不同的功能.页面级的功能通常包括隐藏特定的用户界面元素以阻止未经授权的用户使用某种功能.此外,我们还可以使用一些特性来限制某些用户对类或方法的执行.
祝编程愉快!
Security Tutorials系列文章第七章:User-Based Authorization(下)相关推荐
- Security Tutorials系列文章第七章:User-Based Authorization(上)
本文英文原版及代码下载:http://www.asp.net/learn/security/tutorial-07-cs.aspx Security Tutorials系列文章第七章:User-Bas ...
- cobaltstrike扩展_Cobalt Strike系列教程第七章:提权与横向移动
Cobalt Strike系列教程分享如约而至,新关注的小伙伴可以先回顾一下前面的内容: Cobalt Strike系列教程第一章:简介与安装 Cobalt Strike系列教程第二章:Beacon详 ...
- 第七章、 面向对象基础--下(续) 内部类、枚举、注解
文章目录 内容 学习目标 第七章 面向对象基础--下(续) 7.7 内部类 7.7.1 概述 7.7.1 非静态成员内部类 练习1:语法练习题 练习2:简单面试题 练习题3:高难面试题 7.7.2 静 ...
- TiDB 源码阅读系列文章(十九)tikv-client(下)
上篇文章 中,我们介绍了数据读写过程中 tikv-client 需要解决的几个具体问题,本文将继续介绍 tikv-client 里的两个主要的模块--负责处理分布式计算的 copIterator 和执 ...
- Lync Server 2010的部署系列_第七章 部署边缘服务器(上)
一.配置边缘支持的内部DNS记录 1) 登录DC.Gianthard.com(192.168.1.11).在相应的 DNS 服务器上,依次单击"开始"."控制面板&quo ...
- c语言file_C语言 技能提升 系列文章(七)格式化输入/输出
当初大家入门学习C语言的时候,面对一个黑黑的命令行界面,仅有的交互方法只有printf/scanf这两个函数作为输入输出.但是,大家知道吗?这两个函数后面还有很多兄弟姐妹.那就是C语言的格式化输入输出 ...
- 【Laravel-海贼王系列】第七章,Pipeline 类解析
Pipeline Laravel 的中间件是通过管道类来实现的. 通过内核处理请求的过程中管道的作用来解析管道类! protected function sendRequestThroughRoute ...
- 因果系列文章(7)——干预工具(下)
在上一节我们继续学习了有关干预的知识,具体来说,学习了三种重要的干预工具:后门调整.前门调整.逆概率加权.在本节中,我们从线性系统开始入手,介绍变量连续时的因果效应如何表达,并学习中介.工具变量等概念 ...
- K3S 系列文章-RHEL7.8 离线有代理条件下安装 K3S
一 基础信息 1.1 前提 本次安装的为 k3s 1.21.7+k3s1 VM 版本为 RHEL 7.8, 7.9 或 8.2, 8.3, 8.4(K3s 官网要求) VM YUM 仓库:已配置对应版 ...
最新文章
- Python 获取接口数据,解析JSON,写入文件
- 选购维生素C需要注意的事项
- python在统计专业的应用_Python统计学一数据的概括性度量详解
- 渝粤教育 陕西师范大学 《初级微观经济学》作业
- [HNOI 2001]求正整数
- scala 判断字段 是不是 日期类型_scala 使用指南,降低新手入门难度
- java xmpp openfire_java应用之openfire入门篇
- 安装完最小化 RHEL/CentOS 7 后需要做的 30 件事情(四)
- 深度学习入门笔记(8)——什么是optim?
- DataMatrix识别及定位项目笔记(2)——基于QT+libdmtx-0.7.5的DataMatrix解码及定位
- 常用颜色大全---RGB值及中英文名称
- C语言算法题:一只小蜜蜂...
- 我说CMMI2.0之:详细剖析(PQA)过程质量保证
- Alan Walker MV 合辑01 by defender 歌词
- [培训-DSP快速入门-3]:C54x DSP内存资源与内存空间分布
- 亲身经历:一次sql缺少where条件的惨案,绩效奖金差点没啦~
- 计算字符串的距离(编辑距离)
- 使用ArchR分析单细胞ATAC-seq数据(第四章)
- 本地游升温,银泰百货成新晋打卡热门地
- 深度强化学习+启发人类的决策智能,专访一家有愿景的中国企业「启元世界」... 1
热门文章
- CentOS7.1 KVM虚拟化之经常使用管理虚拟机命令(3)
- appium入门文档
- android post、get请求数据
- VC 实现文件夹属性的获取与更改
- TypeScript入门教程 之 Let 关键字
- Mblog 开源Java多人博客系统
- C语言,利用一维数组选择法排序,使学生成绩高低排序(要求输入为负值时输入结束)
- Robot Framework自动化测试框架核心指南-如何使用Java编写自定义的RobotFramework Lib
- JavaScript-打开新窗口(window.open)
- CAS自旋锁到底是什么?为什么能实现线程安全?