人无信不立/2008-04-26 22:30

永春阁

专注于技术,切不可沉湎于技术

  博客园 :: 首页 :: 联系 :: 订阅 订阅 :: 管理
  213 Posts :: 20 Stories :: 1051 Comments :: 64 Trackbacks

以前开发项目时经常是自己开发一套用户权限管理系统进行验证,比较灵活。最近为了单点登录的问题又把Asp.Net自带的验证方式看了一遍,发现这种方式也比较方便,功能也还可以。在Asp.Net提供了三种常用的验证方式:Windows方式是和IIS结合起来可以实现基本、摘要、集成 Windows等身份验证;Passport方式是使用Windows Live ID的帐户来进行统一验证的;Forms方式是使用常见的表单来实现验证。
本文主要就是讨论Forms验证方式普通实现、自定义实现、自定义角色提供程序、如何单点登录(可和MOSS结合)等几个方面。
一、普通实现方式
这种方式是最简单的,只需要配置一下就可以了。
1、执行aspnet_regsql命令建立数据库
aspnet_regsql命令在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727目录下,按提示运行就可以了

2、新建一个web网站
在Web.Config中加入配置:

  <connectionStrings>
    
<add name="MySqlConnection" connectionString="Data Source=dbserver;Initial Catalog=database;user id=userid;password=****;" />
  
</connectionStrings>

  
<system.web>
        
<authorization>
            
<deny users="?"/>
        
</authorization>
        
<authentication mode="Forms">
            
<forms loginUrl="login.aspx" name=".ASPXAUTH"/>
        
</authentication>

    
<membership defaultProvider="SqlProvider">
      
<providers>
        
<clear />
        
<add connectionStringName="MySqlConnection" applicationName="MyApplication"
          enablePasswordRetrieval
="false" enablePasswordReset="true" requiresQuestionAndAnswer="true"
          requiresUniqueEmail
="true" passwordFormat="Hashed" name="SqlProvider"
          type
="System.Web.Security.SqlMembershipProvider" />
      
</providers>
    
</membership>

  
</system.web>
主要就是指定Forms验证使用的数据库,如果不指定数据库会使用本机默认的aspnetdb 数据库
deny users="?"表示不允许匿名用户访问,也就是说当匿名用户访问时自动跳转到下面配置的login.aspx页面。
至于authorization和authentication节的其他属性可以参考MSDN,里面有很详细的介绍。

3、在网站里创建Default.aspx和Login.aspx页面
在Login.aspx页面里面放入LoginCreateUserWizard控件(因为我们新建的库中一个用户也没有,CreateUserWizard控件只是用来建立测试用户的,建好用户后可以把这个控件删除)
在Default.aspx页面中随便放入一些内容。
当我们访问Default.aspx时就会自动转入Login.aspx进行验证了。

二、自定义实现方式
采用第一种方式时会要求建立一个数据库,很多表,可能并不符合我们自己的业务要求。可以使用以下的自定义方式
1、利用Login控件的Authenticate事件
这个事件就是用来进行验证的,可以通过指定true值表示验证通过:
    protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
    
{
        
//判断用户名密码是否正确
        
//
        e.Authenticated = true;
    }
2、完全抛开Login等控件,自己写代码
其实Login控件的核心主要也就是往Cookie里面放入一些值,那么我们可以在自己的代码中来进行这个操作:
    protected void Button1_Click(object sender, EventArgs e)
    
{
        
//判断用户名密码是否正确
        
//.
        FormsAuthentication.SetAuthCookie(userName, false);
        
if (Context.Request["ReturnUrl"!= null)
        
{
            Response.Redirect(Context.Request[
"ReturnUrl"]);
        }

        
else
        
{
            Response.Redirect(FormsAuthentication.DefaultUrl);
        }

    }
采用以上两种方式就不用建立默认的数据库了,直接使用我们的逻辑进行验证操作

三、自定义角色提供程序
以上说的都是用户级别的验证,在有的情况下需要根据角色来进行验证,比如指定某个目录或某个aspx文件只能让哪几个角色的用户访问,根据角色来控制的话比较方便灵活。
1、在登录验证的时候把角色信息也保存到Cookie中去:
    protected void Button1_Click(object sender, EventArgs e)
    
{
        
//判断用户名密码是否正确
        
//.

        
//得到用户的角色,测试时暂时写死
        string userRoles = "Admins,testst"
        FormsAuthenticationTicket Ticket 
= new FormsAuthenticationTicket(1, user, DateTime.Now, DateTime.Now.AddMinutes(30), false, userRoles, "/");
        
string HashTicket = FormsAuthentication.Encrypt(Ticket); 

        
//把角色信息保存到Cookie中去
        HttpCookie UserCookie = new HttpCookie(FormsAuthentication.FormsCookieName, HashTicket);
        Response.Cookies.Add(UserCookie);

        
if (Context.Request["ReturnUrl"!= null)
        
{
            Response.Redirect(Context.Request[
"ReturnUrl"]);
        }

        
else
        
{
            Response.Redirect(FormsAuthentication.DefaultUrl);
        }

    }
把角色信息加密成特定的格式保存。
2、自定义角色提供程序
如果要按照角色进行验证的话,肯定要涉及到角色提供程序,在默认情况下也是会去连接默认的数据库的,我们可以自己写一个角色提供程序来实现自己的逻辑。
首先在web.config中加入配置:
Code
这个就是指定我们的角色提供类MyRoleProvider。
这个类必须从System.Web.Security.RoleProvider继承,只要重载实现一个方法就可以了(其他方法返回异常):
    public override string[] GetRolesForUser(string username)
    
{
        FormsIdentity Id 
= HttpContext.Current.User.Identity as FormsIdentity;
        
if (Id != null)
        
{
            
return Id.Ticket.UserData.Split(new Char[] ',' });
        }

        
return null;
    }
也就是从我们之前保存到Cookie中的值取得用户角色(FormsAuthentication会自动把保存的cookie转化成User内的值)

之后我们就可以在web.config中配置角色验证规则了:
  <location path="admin">
    
<system.web>
      
<authorization>
        
<allow roles="Admins"/>
        
<deny users="*"/>
      
</authorization>
    
</system.web>
  
</location>
或者也可以在代码中判断:
bool a = User.IsInRole("testt");
判断起来还是很方便的。

四、单点登录
使用Forms的单点登录主要是通过machineKey的配置,machineKey 元素对密钥进行配置,以便将其用于对 Forms 身份验证 Cookie 数据和视图状态数据进行加密和解密,并将其用于对进程外会话状态标识进行验证
使用这种方式的单点登录目前只能实现相同主机或相同子域站点之间的同步登录,比如www.cnblogs.com和firstyi.cnblogs.com可以实现,但是www.cnblogs.com和www.sina.com.cn就不能实现了,对于非同一父域名下的域名间不能跨站登录
主要配置如下:
  <machineKey validationKey="282487E295028E59B8F411ACB689CCD6F39DDD21E6055A3EE480424315994760ADF21B580D8587DB675FA02F79167413044E25309CCCDB647174D5B3D0DD9141" decryptionKey="8B6697227CBCA902B1A0925D40FAA00B353F2DF4359D2099" validation="SHA1"/>
  
<authentication mode="Forms">
      
<forms loginUrl="login.aspx" name=".ASPXAUTH1" domain=".cnblogs.com" />
  
</authentication>
要实现单点登录的多个web站点的machineKey必须一样,forms里面的name和domain也必须一样
这样配置好之后,在其中一个站点登录后再调转到另一个站点就不需要再次登录了。
注:如果MOSS网站采用Forms验证方式的话,只要把MOSS站点的对应配置改成和自己的Asp.Net站点一致,那么可以从自己的站点直接进入MOSS站点,也不需要重新登录(MOSS站点和自己的站点要有相同的用户名)

其他:
Forms验证之后可以使用以下方法退出登录:
FormsAuthentication.SignOut(); 

另外这些登录的后台Module是配置在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\web.config文件中的:
      <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule"/>
      
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"/>
      
<add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule"/>

附:
最后的web.config文件



Tag标签: SSO
posted on 2008-04-22 19:56 永春 阅读(2632) 评论(21)  编辑 收藏 所属分类: .Net

Feedback

#1楼  2008-04-22 20:07 ZH-CN      
很好~
  回复  引用  查看    

#2楼  2008-04-22 20:23 侯垒      
学习
  回复  引用  查看    

#3楼  2008-04-22 21:10 zzzzz [未注册用户]
单点登录很多都是在url的querystring中带上加密后的字符,然后解码确认。
  回复  引用  查看    

#4楼  2008-04-22 21:11 Zhuang miao      
:-)

  回复  引用  查看    

#5楼  2008-04-22 21:34 沛沛      

machineKey 是怎么得到的呢?


  回复  引用  查看    

#6楼  2008-04-22 22:02 esupermary [未注册用户]
good
  回复  引用  查看    

#7楼  2008-04-22 22:04 pwrjng      
问下,如何给User自定义Property呢?比如DepartmentID?
  回复  引用  查看    

#8楼  2008-04-22 23:34 new 维生素C.net()      
@pwrjng
用profile
  回复  引用  查看    

#9楼  2008-04-22 23:50 Leem      
用系统自带的这些还是有很多局限性的,而且不够灵活.还是自己写的好.
  回复  引用  查看    

#10楼  2008-04-23 00:39 Yannic Yang      
其实系统自带那一堆扩展性很好的,功能不够可以以及重写
那套东西权限控制有些弱,都得写在web.config里,并且权限控制以页面或文件夹为单位……有时候很不爽
  回复  引用  查看    

#11楼  2008-04-23 08:31 李战      
俺的单点登录没用Forms身份验证,是自己的判断。而且域名间传递信息是不会在URL中看到参数的。

路过,学习了。

  回复  引用  查看    

#12楼 [楼主] 2008-04-23 08:50 永春      
@zzzzz
这样的话不够透明的
  回复  引用  查看    

#13楼 [楼主] 2008-04-23 09:09 永春      
@沛沛
machineKey主要是在单点登录和负载均衡时使用的,只要保证自己的多个web网站一致就可以了

可以用RNGCryptoServiceProvider类来生成随机数的
  回复  引用  查看    

#14楼  2008-04-23 09:19 王孟军!      
不错
这种方式只支持 同主域的 SSO
不同主域的 SSO,请楼主继续

请指教
my email :wmj2212@126.com

  回复  引用  查看    

#15楼  2008-04-23 09:25 生鱼片      
学习
  回复  引用  查看    

#16楼 [楼主] 2008-04-23 09:27 永春      
@pwrjng
@new 维生素C.net()
用profile是可以,不过那是另外一种模式了

其实你也可以和角色一样把DepartmentID放到Cookie中去的(自己定义一些规则)
另外更灵活的实现方式是实现自己的FormsAuthenticationModule类
  回复  引用  查看    

#17楼 [楼主] 2008-04-23 09:30 永春      
@Leem
@Yannic Yang
的确,想灵活的话都可以重写的

其实我们也可以在代码中进行权限判断的,比如在Page_Load时判断:
if (!User.IsInRole("Admin"))
{
throw new Exception(".....");
}
  回复  引用  查看    

#18楼 [楼主] 2008-04-23 09:37 永春      
@李战
@王孟军!
不知道李战兄是怎么实现的?

目前我能想到的不同主域的单点登录是通过Url中传递一个加密的用户名、密码,在站点间跳转时在其他站点的BeginRequest中进行再次验证的
  回复  引用  查看    

#19楼  2008-04-23 13:21 五味果      
好文,绝对要顶。实用~!
  回复  引用  查看    

#20楼  2008-04-23 14:41 镜涛      
曾经尝试过用Form验证,好像那会我在global中写的,不过我现在都用Session
  回复  引用  查看    

#21楼  2008-04-23 15:06