`

在Silverlight+WCF中应用以角色为基础的安全模式(一)基础篇之角色为基础的安全模式简介

阅读更多

  

    引言

  最近一直在研究Silverlight+WCF应用中的安全问题,如何设计一个安全,又符合Silverlight和WCF的规范的应用呢?又可以将以前的角色为基础的开发框架拿来主义呢?

  我们知道WCF在安全方面提供了很多的绑定协议,可是Silverlight3+WCF的话,只有basicHttpBinding可以使用,这就使得我们的选择不多,还有就是项目本身是一个互联网应用,还是使用比较通用的角色为基础的权限系统比较好。

  这个系列有两篇文章,一篇讲解.NET框架提供我们的角色为基础的安全模式,以及如何根据我们的需求,自定义角色为基础的安全模式;一篇讲解在Silverlight+WCF应用中,如何设计的一种角色为基础的应用方法。

  文中的代码下载:/Files/virusswb/RetrieveSecurity_src.zip

  正文

  

 

  .NET中的角色为基础的安全

  .NET 框架使得 你在应用中实现以角色为基础的安全模式非常容易。迫使安全有两部分组成,认证和授权。认证就是验证你的身份。应用程序验证你就是你所声明的人。通常的做法是用户输入用户名和密码,应用查找你输入的用户名,然后验证你输入的密码是否匹配。更高级的做法是依赖生物认证,例如:指纹或者是视网膜,又或者是一张绑定了个人PIN码的认证卡。如果认证失败,用户将不被允许进入系统,除非系统允许匿名访问,意味着如果系统确认了你的身份,就授予你访问权。授权就是确认用户是否能操作系统的某项功能。授权依赖于已知的用户身份以及和用户相关的安全信息,基于这些安全信息,系统就可以批准或者拒绝用户的请求。

  .NET框架提供了通过Identity访问用户信息,通过principal访问授权信息。Thread.CurrentPrincipal提供了当前线程的principal信息,默认情况下,它是一个非认证的授权。框架提供了两种不同的principal,一个是windows principal,一个是通用的授权generic principalWindows principal工作在windows 操作系统上。所以,当前运行的线程会映射到一个windows帐户上。如果你正在运行一个windows form的应用程序,它就是一个用户。

有两个办法可以访问windows principal

// set that a principal should be attached to the thread and

// it should be a windows principal

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

// get hold of the windows principal

WindowsPrincipal MyPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal;

// get the windows identity

WindowsIdentity MyIdentity = MyPrincipal.Identity;

 

  通过调用当前程序域的SetPrincipalPolicy,你告诉框架当前线程需要附加的principal,你需要在第一次访问principal之前做这些设置。调用Thread.CurrentPrincipal返回和当前线程绑定的principal。第一次这么做的时候,框架会查询windows的帐户信息创建一个windows身份和一个windows授权并且绑定到这个线程。从windows principal你可以访问windows identity。另一个办法是

// get an identity object for the windows user

WindowsIdentity Identity = WindowsIdentity.GetCurrent();

// get hold of the windows principal

WindowsPrincipal MyPrincipal = new WindowsPrincipal(Identity);

  WindowsIdentity.GetCurrent()查询windows帐户信息,同时创建一个identity代表当前用户,那样你可以用这个identity创建一个principal。这    样做的缺点就是每次都需要查询windows帐户,然后创建一个identity和一个principal。第一种方法每次都会使用相同的identityprincipal。通用的principal允许你创建不绑定任何windows帐户的一个principalidentity

// create the generic identity GenericIdentity

Identity = new GenericIdentity("Administrator");

// define the roles to associate with the generic principal

string[] Roles = new string[2] { "Manager", "Architect" };

// create the generic principal

GenericPrincipal MyPrincipal = new GenericPrincipal(Identity,Roles);

// bind the generic principal to the thread

Thread.CurrentPrincipal = MyPrincipal;

  首先创建一个通用的identity,你需要提供identity的名称,因为他不绑定任何windows帐户,需要一个用户名。然后定义你想要这个授权拥有的角色,最后创建一个principal,然后提供identity和角色列表。然后你可以将这个授权绑定到当前线程。

创建自定义的授权principal和认证identity

  .NET框架允许通过实现IPrincipalIIdentity接口,来自定义授权和认证。本文下面的代码,将展示如何创建一个数据库驱动的认证和授权。

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype>

  

  授权过程在用户表中检查提供的用户名和密码。授权成功之后,读取用户信息和用户的安全组信息,查看用户属于那些安全组。

  这些信息对于自定义认证和授权都是必要的。但是授权还可以更进一步,还可以检查个人权限信息,例如:用户是否被允许查看预算等。这些信息都是从SecurityRightAssign表中读取出来,让我们先创建一个自定义身份。

public class UserIdentity : IIdentity

{

// the authentication type for us is always database

private static string AuthenticationTypeString = "Database";

// hash table with all the user info we have

private Hashtable UserInfo;

// create the user identity; all user information is in the hashtable passed along

private UserIdentity(Hashtable UserInfo)

{

      this.UserInfo = UserInfo;

}

//create a user identity and return it to the caller

public static UserIdentity CreateUserIdentity(Hashtable UserInfo)

{

      return new UserIdentity(UserInfo);

}

}

 

  UserIdentity实现了IIdentity接口,需要我们事先三个属性。类型的构造函数被私有化,防止通过实例化来构造对象。你需要通过静态方法CreateUserIdentity,传递一个HashTable结构的用户类型,然后创建一个身份的实例。Name属性返回这个身份的名称。

// returns the name of the identity

public string Name

{

  get

  {

    return

    Convert.ToString(UserInfo[UserNameKey],CultureInfo.InvariantCulture).Trim();

  }

}

// returns if identity is authenticated or not

public bool IsAuthenticated

{

  get

  {

    return true;

  }

}

// the type of authentication

public string AuthenticationType

{

  get

  {

    return AuthenticationTypeString;

  }

}

  IsAuthenticated属性返回用户是否被认证通过,在上面的代码中用户总是被认证功过,因为我们return true。如果你允许匿名访问,你就可以为匿名用户设置为false。最后一个属性AuthenticationType返回的是验证的类型,在我们的代码中返回的是“Database”。WindowsIdentity返回的是NTLMGenericIdentity返回的是空字符串或者是实例化GenericIdentity的时候传递的验证类型。下面,我们里实现自定义的principal

public class SecurityPrincipal : IPrincipal

{

// stores the list of security rights the user belongs too

private Hashtable SecurityGroups;

// stores the list of security rights the user has

private Hashtable SecurityRights;

// the user identity we create and associate with this principal

private UserIdentity TheUserIdentity;

// constructor: stores role and permission info and creates custom identity

private SecurityPrincipal(Hashtable SecurityGroups, Hashtable SecurityRights,

                  Hashtable UserInfo)

{

      this.SecurityGroups = SecurityGroups;

      this.SecurityRights = SecurityRights;

      // creates the IIdentity for the user and associates it with this IPrincipal

      TheUserIdentity = UserIdentity.CreateUserIdentity(UserInfo);

}

// create the security principal and return it to the caller

public static SecurityPrincipal CreateSecurityPrincipal(Hashtable SecurityGroups,

                  Hashtable SecurityRights, Hashtable UserInfo)

{

      return new SecurityPrincipal(SecurityGroups,SecurityRights,UserInfo);

}

}

  实现IPrincipal接口需要实现Identity属性和IsInRole()方法,同样的这个类型的构造函数也是私有的,防止通过实例化来创建对象。你需要调用静态方法CreateSecurityPrincipal,传递一个hashtable类型的用户信息,一个用户所属的角色信息,还有就是用户在系统中的特权。这个类型的构造函数调用自定义的Identity方法的静态函数CreateUserIdentity,将用户信息传递给CreateUserIdentity方法,然后返回一个UserIdentityCreateSecurityPrincipal方法返回一个自定义的principal实例。Identity属性返回和这个principal相关联的identity

// returns the Identity object associated with the principal

public IIdentity Identity

{

      get

      {

            return TheUserIdentity;

      }

}

// checks if user belongs to role

public bool IsInRole(string Role)

{

      return SecurityGroups.ContainsValue(Role);

}

// checks if user has permission

public bool HasPermission(string Permission)

{

      return SecurityRights.ContainsValue(Permission);

}

  IsInRole方法检查用户是否属于角色,是通过检查角色是否在hashtable类型的SecurityGroups中,然后返回true 或者false。我们自定义的principal还实现了一个方法HasPermission,它和IsInRole方法类似,但是检查的是提供的权限是否在特权列表中,然后返回true或者false

这些已经实现了自定义的identityprincipal,下面的代码解释了信息是如何从数据库中读取,最后要做的就是去使用它。

public static IPrincipal SetSecurityPrincipal(Hashtable SecurityGroups,

                               Hashtable SecurityRights, Hashtable UserInfo)

{

// set that we want to use authentication within the current app-domain;

// this means a thread will have a IPrincipal associated which is then

// used by the .NET security classes when checking role based security

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

// we switch to the new security principal only if we didn't do so already;

// protects us from the client calling the method multiple times

if (!(Thread.CurrentPrincipal is SecurityPrincipal))

{

  // create a new instance of the security principal which we can do only

  // within a class member as we marked the constructor private

  SecurityPrincipal TheSecurityPrincipal = new SecurityPrincipal(SecurityGroups,

                  SecurityRights,UserInfo);

  // get a reference to the current security principal so the caller

  //can keep hold of it

  IPrincipal CurrentSecurityPrincipal = Thread.CurrentPrincipal;

  // set the security principal for the executing thread to the newly created one

  Thread.CurrentPrincipal = TheSecurityPrincipal;

  // return the current security principal;

  return CurrentSecurityPrincipal;

}

// return null if we don't switch the security principal

else

      return null;

}

为了使用,我们在SecurityPrincipal类型上提供了一个静态方法SetSecurityPrincipal。首先调用AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

  这样做看起来是错误的,因为我们想要的不是一个windows principal,而是一个自定义的principal。这么做只是为了保证我们拥有一个绑定到当前线程的principal,然后我们检查绑定到当前线程的principal是否是自定义的principal类型。如果是的话,我们什么都不需要做,因为我们已经为当前线程分配了我们自定义的principal,这确保了调用者在多线程的环境中调用不会产生负面的问题。只是在第一次我们会发现没有绑定到自定义的principal,这时候我们创建自定义的principal,创建自定义的identity,并且绑定到当前线程。如果调用者需要的话,我们返回当前principal给他。

  在使用Thread.CurrentPrincipal访问用户信息的时候,会检查用户的角色和特权,这些都可以通过在principal上调用IsInRole或者是访问identity来实现。如果你想检查用户的特权,你可以使用从Thread.CurrentPrincipal中获取的principalHasPermission方法来实现。

public bool CheckSecurityPermission(string Permission)

{

      // if the current IPrincipal is of the same type as our custom

      // security principal then go and check the security right

      if (Thread.CurrentPrincipal is SecurityPrincipal)

      {

          SecurityPrincipal Principal = (SecurityPrincipal)

                                        Thread.CurrentPrincipal;

          // returns whether the user has the permission or not

          return Principal.HasPermission(Permission);

      }

      // if we have a standard IPrincipal in use then we can not check

      // the permission and we always return false

      else

          return false;

}

  如果你正在创建一个新的应用程序域线程,你不想为每一个线程设置自定义的principal,你可以为每一个新创建的线程创建一个默认的principal。设置默认principal一定要在第一次第一次访问principal之前设置Thread.CurrentPrincipal

// create the custom principal

SecurityPrincipal MyPrincipal = SecurityPrincipal.CreateSecurityPrincipal(

                  AppDomain.CurrentDomain.SetThreadPrincipal);

// set the custom principal as the app domain policy

AppDomain.CurrentDomain.SetThreadPrincipal(MyPrincipal);

  你设置默认principal,只需要在应用程序域设置一次。在应用程序域设置多次会引发PolicyException异常。

 

  示例代码

 

  示例代码演示的是一个windows form程序,首先需要用户登录(在数据库中已经有两个用户,virusswb,密码和用户名一致)。btnLogon_Click()事件和btnLogin按钮关联,调用DataLayer.CheckUserNameAndPassword().用来验证用户,调用DataLayer.RetrieveUserInformation().来获取用户信息,最后通过调用DataLayer.RetrieveSecurityInformation().来获取用户所属的角色和权限信息,在获取了用户信息、角色信息和权限信息之后,使用SecurityPrincipal.SetSecurityPrincipal()创建一个principalidentity,并且绑定到当前线程。

  从示例中看出用户属于是三个角色,全部的权限,和用户信息,可以检查用户是否属于某一个角色,是否具有某一个权限,CheckSecurityRoles() and CheckSecurityPermissions()返回用户是否属于一个角色,是否有一个权限。logoff 按钮的 LogOff_Click()方法恢复原始的principal,并且返回登陆界面,允许另外一个用户登录,继续前面的处理过程。

  在示例文件夹中你会发现一个叫做RetrieveSecurity.bak的文件,它是数据库的备份文件。恢复数据库,配置app.config文件中的连接字符串。你可以在数据库中添加用户、角色和权限信息。示例展示了在.NET 的角色为基础的安全模型之后,如何实现数据库驱动的验证和安全模型。

 

  总结

  大多数应用都需要通过角色和权限来实现用户验证和安全模型。.NET框架使得这些变得容易,几行代码,就改变了windows账号和安全组的影响。使用自定义的identityprincipal可以很容易的扩展角色为基础的安全框架,示例代码展示的就是如何实现数据库驱动的角色权限系统。

  参考文献

  【1】Role-Based Security  Microsoft

  【2】Introduction to Role-Based Security in .NET  Klaus Salchner

  【3】在Identity 增加自己的属性 部门,并且使用access mdb文件实现角色验证  iHqq

 

  感谢上面这些机构和作者的无私奉献。

分享到:
评论

相关推荐

    Silverlight程序中动态设置WCF服务地址

    同时,需要注意的是,由于Silverlight的安全限制,跨域访问可能需要配置WCF服务的跨域策略文件(crossdomain.xml)和Silverlight应用程序的客户端access policy文件(clientaccesspolicy.xml),以允许从不同源进行...

    Silverlight WCF RIA服务(二十二)Silverlight 客户端 3 源代码

    在本篇中,我们将深入探讨Silverlight客户端与WCF RIA服务的交互,特别是通过查看提供的"ExampleSilverlightApp"源代码来理解这一过程。Silverlight是微软开发的一个富互联网应用程序(RIA)平台,它允许开发者创建...

    oa.rar_JMail44_pro.exe_oa_silverlight oa_wcf_wpf OA

    本篇将深入探讨一款名为"oa.rar"的压缩包文件,其中包含了"JMail44_pro.exe"、"oa_silverlight"、"oa_wcf"以及"oa_wpf"等关键组件,揭示了新一代OA软件的核心技术特点。 首先,"JMail44_pro.exe"是一款专业的企业级...

    一步一步学习Silverlight之基础知识篇

    【Silverlight基础知识篇】 Silverlight是由Microsoft开发的一种富互联网应用程序(RIA)平台,它主要用于创建交互式的、多媒体丰富的网络应用。Silverlight 2 Beta 1的发布带来了许多新特性和改进,增强了其作为...

    WCF RIA 服务 (二十五)-- Silverlight 客户端 6 源代码

    在本篇中,我们将深入探讨WCF RIA(Windows Communication Foundation Rich Internet Application)服务与Silverlight客户端6的源代码交互。WCF RIA服务是一种强大的技术,它允许开发者构建跨域分布式应用程序,尤其...

    Silverlight.3.高级编程(C#篇)2pdf

    《Silverlight 3 高级编程 (C#篇)》是针对Microsoft Silverlight 3这一技术的深入探讨,主要面向已经掌握基础的C#开发者,旨在帮助他们提升在Silverlight平台上的开发技能。这本书的第二部分继续了第一部分的内容,...

    silverlight学习资料

    在深入学习Silverlight的过程中,你还会遇到诸如数据服务集成(如WCF RIA Services)、多媒体播放、动画和特效、以及与服务器端的交互(如ASP.NET MVC或Web Forms)等主题。Silverlight的灵活性和强大功能使其成为...

    silverlight mvvm demo

    在现代软件开发中,Model-View-ViewModel(MVVM)设计模式已经成为构建用户界面的一种主流方式,尤其在Silverlight应用开发中,MVVM模式的应用尤为广泛。本篇文章将通过分析“silverlight mvvm demo”这个学习示例,...

    silverlight(版权归陈敏所有)

    4. **异步通信**: Silverlight中的WCF通信通常采用异步模式,以避免阻塞UI线程,确保用户界面的响应性。 ### 三、文件上传与下载 1. **Upload**: Silverlight提供了FileOpenPicker控件,允许用户选择本地文件进行...

    silverlight resource

    这篇论文可能涵盖了Silverlight的基础技术,包括XAML(可扩展应用程序标记语言)、数据绑定、依赖属性、动画和特效,以及与其他技术如WCF(Windows Communication Foundation)的集成。 **八、应用Silverlight2_0和...

    6Silverlight访问数据库之MySQL数据库.pdf

    而数据库访问是这类应用的基础,本篇文章将探讨如何使用Silverlight与MySQL数据库进行数据交互,实现CURD(Create、Read、Update、Delete)操作。 首先,我们需要在开发环境中搭建测试项目。这通常涉及到创建一个新...

    silverlight4学习源码

    Silverlight,作为微软推出的一款跨浏览器插件,曾广泛应用于富互联网应用(RIA)开发,尤其是在Windows Presentation Foundation(WPF)的基础上提供了轻量级的Web应用程序开发框架。本篇文章将围绕“Silverlight4...

    Silverlight 项目练习入门

    在Visual Studio 2010中,选择“新建项目”,然后选择“Silverlight应用程序”,并命名为“SilverlightDB”。在创建过程中,可以选择项目类型,例如SL4 Application。这样,你就有了一个基础的Silverlight项目结构。...

    silverlight 4 相關書籍

    在本篇文章中,我们将深入探讨 Silverlight 4 的核心特性,以及与其相关的 Model-View-ViewModel(MVVM)设计模式在实际开发中的应用。** **1. Silverlight 4 的新特性:** - **增强的浏览器集成**:Silverlight 4 ...

    Silverlight 学习资料

    此篇文章介绍了如何在Silverlight项目中使用LINQ进行数据处理,对于希望提升Silverlight应用数据管理能力的开发者而言,是一个不可多得的参考资料。 3. **《WCF与Silverlight的深度集成》**:WCF(Windows ...

    一步一步学Silverlight 2系列(4)

    Silverlight 2是微软推出的一种富互联网应用程序平台,它在Beta 1版本中引入了多种新特性,包括对多种编程语言的支持(如Visual Basic、Visual C#、IronRuby、IronPython),以及对JSON、Web Service、WCF和Sockets...

    ArcgisApi.Silverlight.App.zip

    ArcGIS API for Silverlight是一种强大的工具,用于在Web浏览器中构建具有地理空间功能的应用程序。本篇将深入探讨如何利用该API进行开发,首先从平台搭建开始,然后逐步构建你的第一个应用程序。 一、平台搭建 1....

    SilverLight和BingMap做的车辆监控代码.zip

    本篇文章将深入探讨一个利用SilverLight和BingMap技术构建的车辆监控系统,帮助读者理解如何结合这两种技术实现高效、实时的车辆定位和跟踪功能。 SilverLight是微软推出的一种富互联网应用程序(RIA)框架,主要...

    silverlight实现图片公告栏

    本篇文章将深入探讨如何在Silverlight 4中实现一个图片公告栏的功能,这是一项能够以动态、吸引人的方式展示多张图片的应用。 首先,我们要理解Silverlight 4相较于之前的版本有哪些改进和增强。Silverlight 4提供...

Global site tag (gtag.js) - Google Analytics