角色非平行就是如学校的教师与学生,在一个系统里,这两者是不相容的,在以往的系统中,往往是按照实体建立类,建立管理类对实体有增删改查功能,通过权限来限制访问,如一条新闻,教师有增删改查,学生可以查,还有教师开课,学生选课,往往权限设置很复杂,而且这些功能在需求分析时就已经固定了,通过配置来得到的灵活性并无大的意义。

 

如某某老师修改新闻,ID是多少

在现实生活中,我们说话往往不是按照实体管理-功能来说,判断有没有权限,然后 NewsManage/Edit/123

而是以角色-功能来说,因为权限往往已经隐含在角色中了 -> Teacher/EditNews/123

 

之前写过一篇 角色是用户分组还是功能分组,我实现的一个按功能分组的角色管理的思考 ,当时只是有这个想法,走了不少弯路,最终还是完成了整个系统的开发。

 

在新完成的系统中,抛弃了DAL BLL UI 这样的三层架构,使用核心层,角色层,和UI层架构,核心层还是使用传统方式,对实体进行处理,我选用linq to sql,

以Project为例,DataAccess.Project 是linq自动生成的实体,  Entity.Project 是自己定义的实体,用于层间传递

 

代码
       Public Shared Function RetrieveProject(ByVal predicate As Expression(Of Func(Of DataAccess.Project, Boolean))) As List(Of Entity.Project)

            
Using dataContext As New DataAccess.ProjectManageDataContext(ConnString)

                dataContext.ObjectTrackingEnabled 
= False

                
Dim result = From p In dataContext.Projects.Where(predicate) _
...
                             
Select New Entity.Project With { _
                             .ProjectId 
= p.ProjectId, _
...
                             }

                
Return result.ToList

            
End Using

        
End Function

        
Public Shared Sub UpdateProject(ByVal predicate As Expression(Of Func(Of DataAccess.Project, Boolean)), ByVal action As Action(Of DataAccess.Project, Entity.Project), ByVal ep As Entity.Project)

            
Using dataContext As New DataAccess.ProjectManageDataContext(ConnString)

                
Dim projects = From p In dataContext.Projects.Where(predicate)
                
For Each project In projects
                    action(project, ep)
                
Next

                dataContext.SubmitChanges()

            
End Using

        
End Sub


        
Public Shared Function InsertProject(ByVal project As Entity.Project) As Integer

            
Using dataContext As New DataAccess.ProjectManageDataContext(ConnString)

                
Dim dbProject As New DataAccess.Project

                dbProject.DutyOrganization 
= project.DutyOrganization
...
                dataContext.Projects.InsertOnSubmit(dbProject)
                dataContext.SubmitChanges()

                
Return dbProject.ProjectId

            
End Using

        
End Function

        
Public Shared Sub DeleteProject(ByVal predicate As Expression(Of Func(Of DataAccess.Project, Boolean)))

            
Using dataContext As New DataAccess.ProjectManageDataContext(ConnString)

                dataContext.Projects.DeleteAllOnSubmit(dataContext.Projects.Where(predicate))
                dataContext.SubmitChanges()

            
End Using

        
End Sub

 

 

角色层根据实际情况进行继承,教师和学生可继承与用户基类,管理员可以从教师派生

如教师

 

代码
    Public Class Teacher
        
Inherits User

        
Public Function GetMyDirectorProject() As ReadOnlyCollection(Of Entity.Project)

            
Return New ReadOnlyCollection(Of Entity.Project)(DataAccess.AccessProjectHelper.RetrieveProject(Function(p As DataAccess.Project) p.Director = Me.Id))

        
End Function

    
End Class

 


目前正在思考把asp.net mvc模式与这个结合,在mvc模式中,控制器非常适合作为角色来使用,这样网址也非常清晰的显示为角色/功能/参数了

posted @ 2010-03-25 11:07 我想我是风 阅读(1166) 评论(12) 编辑

昨天看到一篇 预防SQL注入攻击之我见  ,发现好多人认为防止sql注入很麻烦,要考虑编码等等。

 

sql注入的本质上是程序员写的拼凑的SQL语句的某个部分需要一个字面值,而这个字面值却是由最终用户决定,最终用户可以构造一个错误的字面值来改变这条SQL语句的语义。

 

字面值有哪些呢?一般就是数字和字符串,日期在access里可以用#符号包含进来作为字面值,在sqlserver里似乎不能直接作为字面值。

预防SQL注入攻击之我见 里的数字处理其实不全面,全转为整数,如果是浮点数怎么办?

 

那么如何防止sql注入呢?让字面值不提前出现闭合符号,即单引号里不能出现单引号(替换一个为两个单引号,数据库本身转义),#里不能出现#,但是数值型的比较麻烦,没有符号将它包含起来,导致和命令的语法一致,那么只能将输入转换为原来的类型才处理了。日期型在sqlserver里,一般是通过CAST('2006-04-04 15:50:59.997' AS datetime) AS UsingCast等方法转换后处理的,所以既可以通过转换为日期来判断,也可以过滤单引号来处理注入。

如果愿意的话,把数值型也作为字符串处理,统一通过过滤单引号也是可行的,效率可能会有所下降, where Id='50',Id数据库类型是整型,也是会隐设转换的

 

特殊情况,比如 where id in (1,2,3) 如果括号里自行输入,那么输入select id from [表名],会将全部记录输出,不过这种情况也没有改变语义,但是如果输入 1);delete from [表名];select (1 这样就改变语义了,究其原因,还是由于字面值的界定符被别人闭合了,这种情况我们就要处理()括号符号了。

 

 

 

posted @ 2009-11-27 13:20 我想我是风 阅读(2352) 评论(31) 编辑

不管是windows自身的权限,还是asp.net自带的role提供程序,都是实现的用户分组,管理员组也好,普通用户组也好,能实现的功能都是一样的,只不过受到权限的限制,权限低的组人为的屏蔽了一些功能。

 

考虑我们自己开发的一些管理系统,角色往往是固定的,而功能又是完全不同的,比如学校里,系统一般总有教师角色,学生角色。而教师和学生实现的功能完全是不同的,也许教师是开课,学生是选课,不太可能一个用户即隶属与教师角色,又隶属于学生角色。

 

在系统的业务逻辑层中,一般的开发似乎也不包含角色的相关信息,只是实现了功能,而谁有调用的权限,交给UI层处理,比如项目的管理,一般总是有个项目管理类 ProjectManage,有Add增加项目方法,Del删除项目方法等,从来不管学生是不能增加项目的权限等情况。

 

根据以上情况,我实现了一个将登录用户角色和业务逻辑层的角色结合起来的模式。

业务逻辑层弱化项目管理类,强化角色的概念,一个业务逻辑层主要实现各角色相关的功能,比如Teacher类,实现AddProject方法,而Student类,就没有实现这个方法。通过角色基类,实现公共代码的共享,通过一些虚函数,还能减少不少界面的麻烦,比如基类定义GetNavigation方法,各角色重写这个方法,实现各自登录的界面栏目,这样UI就不用判断是什么角色,主要得到这个返回值,格式化一下显示就可以了。

 

登录验证的时候把角色信息写到Ticket的UserData里去,这儿是Student,根据不同登录情况,设置不同角色。

            Dim ticket As New FormsAuthenticationTicket(1, TextBox1.Text, DateTime.Now, DateTime.Now.AddMinutes(30), False"Student", FormsAuthentication.FormsCookiePath)
            
Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
            Response.Cookies.Add(
New HttpCookie(FormsAuthentication.FormsCookieName, encTicket))
            Response.Redirect(FormsAuthentication.GetRedirectUrl(TextBox1.Text, 
False))

在登录后,如果有的用户有多个角色,比如,有的教师还是管理员,那么还可以切换一下这个Ticket,就是做个按钮,单击后重新设置UserData,不过登录的用户名就可以直接用User.Identity.Name了。

 

在业务逻辑层有对应的角色类,基类为UserBase

 

Public MustInherit Class UserBase

    
Private _id As String = String.Empty
    
Public Property Id() As String
        
Get
            
Return _id
        
End Get
        
Set(ByVal value As String)
            _id 
= value
        
End Set
    
End Property

    
Private _name As String = String.Empty
    
Public Property Name() As String
        
Get
            
Return _name
        
End Get
        
Set(ByVal value As String)
            _name 
= value
        
End Set
    
End Property

    
Public MustOverride Function GetNavigations() As NavigationCollection

End Class

学生角色,教师角色等分别继承他,并重新GetNavigations等方法。

 

Partial Public Class Student

    
Public Overrides Function GetNavigations() As NavigationCollection

        
Dim nc As New NavigationCollection

        nc.Add(
New Navigation() With {.Title = "项目申请", .Content = "项目申请"})
        
Return nc

    
End Function

End Class

Partial Public Class Teacher

    
Public Overrides Function GetNavigations() As NavigationCollection

        
Dim nc As New NavigationCollection

        nc.Add(
New Navigation() With {.Title = "项目开设", .Content = "项目开设"})

        
Return nc

    
End Function

End Class

 

UI做个登录角色和业务逻辑角色的转换

 

    <Extension()> _
    
Public Function ToBLLRole(ByVal identity As IIdentity) As BusinessLogicLayer.UserBase

        
Dim UIRole = CType(identity, FormsIdentity).Ticket.UserData

        
Select Case UIRole
            
Case "Student"
                
Dim s As New BusinessLogicLayer.Student
                
Dim uu = 取得用户信息的类.GetStudentById(identity.Name)

                s.Id 
= uu.Id
                s.Name 
= uu.Name

                
Return s

            
Case "Teacher"
                
Dim t As New BusinessLogicLayer.Teacher
                
Dim uu = 取得用户信息的类.GetTeacherById(identity.Name)

                t.Id 
= uu.Id
                t.Name 
= uu.Name

                
Return t

'这个根据角色不同而增加


            
Case Else
                
Throw New ArgumentException

        
End Select

    
End Function

 

页面绑定这个返回值

 

            Dim bllRole = User.Identity.ToBLLRole

            xxx.DataSource 
= bllRole.GetNavigations
            xxx.DataBind()

 

posted @ 2009-11-20 09:51 我想我是风 阅读(1603) 评论(2) 编辑
部门要上统一身份认证,统一门户,统一数据库的所谓数字化某某某的东西,可设计的理念和我想的相距甚远
关于统一身份认证的做法,公司是这样做的,逻辑还是相对比较简单的
一个统一认证的点,可以根据用户名和密码返回真或假,用户角色是各系统独立自行判断,可以根据用户名得到用户的更进一步信息,包括所属部门信息,用户只有实体用户,没有虚的岗位用户,都自行由角色去整合。
=============
作为开发人员,我觉得存在很多问题,我的统一身份认证的想法是可以有部分的角色功能,简化各系统的开发
统一认证包括用户,角色,所属部门,分别处理谁,能干什么,能干那些。根据用户名密码能得到角色和所属部门。
1、用户有统一数据库里的实体用户,也要有虚的岗位用户,这些岗位用户由各应用系统申请建立,有些共性的岗位用户可以相互共享。(公司认为虚的岗位用户更难管理,可以通过角色给实体用户已岗位用户的角色,我是认为这样会公私不分的,一个用户登录邮箱,既有自己的私人邮件,又有办公邮件,似乎是件更麻烦的事,人员调动还有修改角色,也是很麻烦。)
2、角色是用户的集合,用户权限的设置都应该放在角色上,系统应该能够返回基本的角色信息,便于简单系统可以免自行设置角色的麻烦,比如在学校,登录后能返回“教师”或者“学生”,虚的岗位用户可以在加入的时候直接设置角色名称。
3、部门是最基础的数据,分级处理,00,0001,0002这样,0001是00的下级部门,用户根据用户的登录情况,进行信息的分类和过滤显示,而不能直接在上面做权限的设置。部门应该比较稳定,应该根据实际存在的情况进行分类(公司按照公章分类,而公章是有大量同一套人,两个公章的概念的,行政和党委很多时候是一个人,而且外面的同级有联系的部门,或者上级部门,直接用“其他类”来表示),部门作为基础数据,应该是可以不通过电脑直接读的,用字母表示应该更好,就像域名cn中国,tw台湾等,但国际区号我就不知道也很难记住了,而且如果给各部门建文件夹,做Logo等情况,用字母表示的代码一眼就知道是那个部门(公司却认为字母会重叠,数字好,他们倒是有业务构件系统,但数字化不是仅仅建立在他这个公司上面的呀,而且还把排序的功能赋给了部门,根据数字的大小排序,中间留空,备部门添加的时候插入用。我是认为部门不应该有排序信息,排序由各应用系统根据另外的文件规定自行排序)

关于部门编码,我也是有个疑惑,两个领导的部门到底如何编码合理,单根的树下去我觉得很不错,但对于比如市气象局,到底父代码作为省气象局还是市政府?
posted @ 2009-07-24 10:55 我想我是风 阅读(1973) 评论(19) 编辑

    ASP.NET 2.0 里的菜单控件加上 sitemap 文件为网站导航提供了很简单的实现方法,但有个令人烦恼的事不能控制菜单打开的窗口,经常有需要弹出新窗口的菜单就比较难处理。其实有个很简单的方法。
  
    首先在sitemap文件添加自己定义属性,这里我们用 target <siteMapNode title="首页" description="" target="_blank" url="~/Default.aspx" /> 然后为 Menu 控件添加 MenuItemDataBound 事件处理代码
  
  protected void MyMenu_MenuItemDataBound(object sender, MenuEventArgs e)
  {
   string target = ((SiteMapNode)e.Item.DataItem)["target"];
   if (target != null && target.Length > 0) e.Item.Target = target;
  }

文章出处:http://www.diybl.com/course/4_webprogram/asp.net/asp_netshl/200725/8783.html

posted @ 2009-07-19 14:39 我想我是风 阅读(108) 评论(0) 编辑
摘要: 现在的asp.net混杂着WebForm,MVC,与AJAX,在做网站的时候,这三种模式似乎不太兼容,整理一下思路,业务逻辑和数据存储不考虑,只考虑分层的UI层应用。WebForm是服务器执行全部的界面效果,客户端只是显示。纯粹的AJAX是客户端执行全部的界面效果,服务器只提供显示的数据。MVC是客户端和服务器端都不考虑界面效果,客户端请求,服务器端产生的数据,并结合已经完成的模板页面发送出去。那...阅读全文
posted @ 2009-05-20 16:40 我想我是风 阅读(2438) 评论(25) 编辑
摘要: [代码]阅读全文
posted @ 2008-11-03 08:39 我想我是风 阅读(322) 评论(0) 编辑
摘要: 做个网站管理的软件,首先是建立独立的帐号[代码]阅读全文
posted @ 2008-11-03 08:37 我想我是风 阅读(163) 评论(0) 编辑