安全是每一个企业应用开发者都需要关注的问题,.NET Framework在WebApplication开发中已经提供了4种认证方式:Windows,Forms,Passport,None。当我们开发的应用是适用于Internet时,Forms认证是一个很好的选择。不过如果开发的应用适用于企业内部网,那么我们就应该优先考虑Windows集成认证方法,域用户、组帐号信息就可以非常方便的集成到应用中。
本文讲解如何配置WebApplication、IIS来实现集成认证方式,以及如何对用户和组进行授权。
基础知识:
ASP.NET,IIS
应用场景说明:
这里假设整个应用的结构为如下所示,Web Application访问Web Service,Web Service访问数据库服务器。客户端到Web Application采用Windows集成认证,Web Application到Web Service也采用集成认证,Web Service到SQL Server采用预先设置好的账号或者Windows集成认证方式(这里的Windows集成认证不等同于前面的2个集成认证,前2个集成认证指的是用户的凭证,而这里的集成认证指的是Web Service所在机器的凭证)。
Web Application的配置:
为了达到集成认证的目的,让用户凭证能够传递到Web Application上来,我们需要对IIS和web.config作如下配置:
1. 对Web Application在IIS中的安全设置如下:
取消IIS的匿名访问设置,并且允许集成Windows身份认证选项
2. 修改Web Application的web.config如下:
2.1 <system.web>
<authentication mode="Windows" />
</system.web>
2.2 <system.web>
<identity impersonate="true" />
</system.web>
说明:
第1步是让用户凭证传递到Web Application上来
第2步则是让用户凭证能够继续传递到Web Service上去(这里通过使用伪装,可以让Web Application的凭证成为用户的凭证,而不再是ASPNET账号凭证)。
此时,System.Security.Principal.WindowsIdentity.GetCurrent().Name与 Page.User.Name保存的是同一个账号信息,即用户的凭证。
3. 为了完成凭证能够继续传递到Web Service中,我们需要做一定的编码工作,因为访问Web Service需要认证。在Web Application中的Web Service代理类中加入如下语句,这样就会把凭证带到Web Service上,否则会出现一个拒绝访问的错误。
wsproxy.Credentials = CredentialCache.DefaultCredentials;
Web Service的配置:
我们也需要对Web Service所在的服务器做一个类似的配置:
1. 对Web Application在IIS中的安全设置如下:
取消IIS的匿名访问设置,并且允许集成Windows身份认证选项(只选择该项,不要选择其他选项)
2. 修改Web Application的web.config如下:
2.3 <system.web>
<authentication mode="Windows" />
</system.web>
2.4 <system.web>
<identity impersonate="false" />
</system.web>
说明:
第1步是让用户凭证传递到Web Service上来
第2步则是让用户凭证不再继续传递下去,连接数据库的账号采用其他的凭证。
此时,System.Security.Principal.WindowsIdentity.GetCurrent().Name保存的是ASPNET账号,而 Page.User.Name则是传递过来的用户凭证帐号。
3. Web Service访问SQL Server,我们可以选择Windows集成认证,也可以选择SQL Server的登录账号访问。在这里的Windows集成认证不再是传递到Web Service中的用户凭证,而是ASPNET账号。
4. 如果某些Web Method只能够允许特定的组或者用户访问(比如工资的详细信息我们只允许某个组的用户访问),那么我们可以采用如下编码来限制访问用户:
[WebMethod][PrincipalPermission(SecurityAction.Demand,Role=@"DomainName\SomeGroup)]public DataSet RetrieveSalaryDetails(){}
采用动态授权方式:
如果Web Application的授权是一种动态方式,即通过页面上提供的设置功能来设置哪些用户(或组)对哪些数据具有什么权限,那么我们还需要了解一些其他的安全编程的知识。
其中比较重要的就是,当新增一个用户(或组)的授权,如何去判断这个账号是否在域(或本机)中存在,这里我们可以使用LookupAccountName这个Windows API。需要注意的是为了查找一个账号是否存在,需要2次调用LookupAccountName这个API,第一次是取得一些所需要的Sid,DomainName的长度信息,第2次才能够取得账号的Sid和DomainName(Sid是每一个账号都拥有的唯一安全表示)。
LookupAccountName首先查找一些知名SID,接着查找built-in和本地账号,再下来查找域账号,最后会查找信任域的账号。因此这个API可以很顺利的解决账号查找的问题。
我们看下面查找账号的代码:
using System.Runtime.InteropServices;
public class ValidateUser{
[DllImport( "advapi32.dll", CharSet=CharSet.Auto, SetLastError=true, PreserveSig=true)]
private static extern bool LookupAccountName(
string lpSystemName,
string lpAccountName,
IntPtr psid,
ref int cbsid,
StringBuilder domainName,
ref int cbdomainLength,
ref int use );
private bool LookUpAccount(string accountName)
{
//pointer an size for the SID
IntPtr sid = IntPtr.Zero;
int sidSize = 0;
//StringBuilder and size for the domain name
StringBuilder domainName = new StringBuilder();
int nameSize = 0; //account-type variable for lookup
int accountType = 0; //get required buffer size
LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType); //allocate buffers
domainName = new StringBuilder(nameSize);
sid = Marshal.AllocHGlobal(sidSize); //lookup the SID for the account
bool result = LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType); if(result) { if(accountName.ToLower().IndexOf(domainName.ToString().ToLower())<0) { accountName = domainName + "\\" + accountName ; } MessageBox.Show("The account is : " + accountName); } else { MessageBox.Show("Can't find the account."); } Marshal.FreeHGlobal(sid); return result; }}
注意:我们可以通过这个API查找出某个账号是否存在,但是查找到的账号可能是很多类型的一种,比如说如果本机机器名和某个域用户同名的话,那么该API查找到的是本机机器账号,因此需要根据返回的accountType来判断一下是否查找到的是用户或组,如果不是,那么应该提示用户输入完整的域账号表达式(DomainName\GroupUserName)。用户的AccountType为1,组的AccountType为4,机器的AccountType为3(详细资料请查找msdn中的LookupAccountName)。
可能的一种安全解决方案:
为了提供一种集成Windows账号的授权策略,在数据库中需要如下几张表:
1.当对某个对象分配一个新账号授权(通过了验证帐号已经存在,授权则是通过分配某些Roles实现),Users表增加一个新行表示该账号,并且产生一些新的Object,User,Role关系来表示对象具有了这个新的授权。
2.当判断某个用户对某个对象是否具有某权限时,需要首先查找出该对象授权的所有用户和组,然后判断用户是否在授权组里,或者是否就是被授权用户。这一部分在Web Service中实现,通过调用Page.User.IsInRole(Group)来判断用户是否属于某个组。
总结:
本文讲述了在Web Application中采用Windows集成认证可能遇到的一些配置和编程方面的问题,通过文章介绍的方法,你可以调整成适合自己的方案来解决你所遇到的问题。配置是实现Windows集成认证的第一步,验证用户账号是否存在,以及用户访问某个对象的权限需要综合考虑用户权限和组权限两方面,这些都在文中作了简单描述。文中所阐述的数据库方案是一个比较简单的实现方法,你可能需要根据自己的需要做一些调整。