共同进退,荣辱与共(技术专栏)

技术专栏

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
 

在开发过程中,权限验证必不可少。

权限管理模型可以参考我原来的文章,大家可以一起讨论下http://www.cnblogs.com/ttcre2/archive/2008/07/24/1250591.html

 

在这里要说的是在系统中如何实现权限管理。

我一般是这么做的。

1.       使用Forms认证,我对session是彻底伤心了。

2.       在页面基类中验证当前用户是否已登录。如果登录了,通过cookie加载用户信息。

3.       通过特性来标识一个页面,如果加上了这个特性,就表示这个页面必须登录后才可以访问。

 

首先配一下web.config,告诉系统登录页和默认页。

 

<authentication mode="Forms">
    
<forms name="CE.SqlString.SqlStringTest" loginUrl="~/Logon.aspx" defaultUrl="~/Default.aspx" />
</authentication>

 

 

用户信息的基类。

 

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Web.Configuration;
using System.Configuration.Provider;

namespace CE.Components.Base.Security
{
    
public abstract class UserInfoBase : ProviderBase
    
{

        
private string userName;
        
private string data;

        
protected UserInfoBase()
        
{

            FormsIdentity identity;
            FormsAuthenticationTicket ticket;

            
//如果应用程序使用forms认证,并且已经登录过了。
            if (SystemWebConfig.AuthenticationMode == AuthenticationMode.Forms && 
                HttpContext.Current.User.Identity.IsAuthenticated)
            
{                

                
//获取cookie中的信息。
                identity = (FormsIdentity)HttpContext.Current.User.Identity;
                ticket 
= identity.Ticket;
                userName 
= ticket.Name;
                data 
= ticket.UserData;
            }

        }


        
protected string Data
        
{
            
get
            
{
                
return data;
            }

        }


        
/// <summary>
        
/// 获取或设置用户的名称。
        
/// </summary>

        public string UserName
        
{
            
get
            
{
                
return userName;
            }

            
set
            
{
                userName 
= value;
            }

        }


        
/// <summary>
        
/// 将经过身份验证的用户重定向回最初请求的 URL 或默认 URL。
        
/// </summary>
        
/// <param name="userName">经过身份验证的用户名。</param>
        
/// <param name="createPersistentCookie">若要创建持久 Cookie(跨浏览器会话保存的 Cookie),则为 true;否则为 false。</param>

        public static void RedirectFromLoginPage(string userName, bool createPersistentCookie)
        
{

            
if (string.IsNullOrEmpty(userName))
            
{
                
throw new ArgumentNullException("userName");
            }


            FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie);

        }


        
/// <summary>
        
/// 将经过身份验证的用户重定向回最初请求的 URL 或默认 URL。
        
/// </summary>
        
/// <param name="userName">经过身份验证的用户名。</param>
        
/// <param name="createPersistentCookie">若要创建持久 Cookie(跨浏览器会话保存的 Cookie),则为 true;否则为 false。</param>
        
/// <param name="userData">存储在票证中的用户特定的数据。</param>

        public static void RedirectFromLoginPage(string userName, bool createPersistentCookie, string userData)
        
{

            FormsAuthenticationTicket ticket;
            
string encTicket;
            HttpCookie cookie;

            
if (string.IsNullOrEmpty(userName))
            
{
                
throw new ArgumentNullException("userName");
            }


            
if (string.IsNullOrEmpty(userData))
            
{
                
throw new ArgumentNullException("userData");
            }


            ticket 
= new FormsAuthenticationTicket
            (
1, userName, DateTime.Now,
            DateTime.Now.AddMinutes(
30), //保存多长时间。
            createPersistentCookie, userData, FormsAuthentication.FormsCookiePath);
            encTicket 
= FormsAuthentication.Encrypt(ticket);
            cookie 
= new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);

            
//是否保存cookie。
            if (createPersistentCookie)
            
{

                
//过期时间同步。
                
//cookie和ticket两个当中任何一个时间失效forms就会认证失效。
                cookie.Expires = ticket.Expiration;
            }


            cookie.HttpOnly 
= true//允许客户端脚本访问。

            HttpContext.Current.Response.Cookies.Add(cookie);
            HttpContext.Current.Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, createPersistentCookie));

        }


    }

}

 

 

 再啰嗦两句。在这个类中2个静态方法RedirectFromLoginPage是用来用户登录的。比如你在登录页面验证了帐号和密码,然后就可以通过该方法登录系统了。如果你是其他页面跳过来的,登录成功后就会跳到刚才那个页面。如果你直接访问的登录页,登录成功后就会自动跳转到默认页面(web.config中设置的那个defaultUrl)。

 

 然后在建立一个AuthenticatedAttribute类。这个类用来标识一个页面是否必须登录后才能访问。

这里用了特性去标识页面,而没用微软的,在webconfig里面配置。主要原因是他只能配到目录一级,而不能到页面 级。

 

using System;
using System.Collections.Generic;
using System.Text;

namespace CE.Components.Base.Security
{

    
/// <summary>
    
/// 用来标记一个页面或一个控件所在的页面是否需要自动验证是否登录。
    
/// </summary>

    [AttributeUsageAttribute(AttributeTargets.Class)]
    
public sealed class AuthenticatedAttribute : Attribute
    
{

        
private bool isAuthenticated;

        
/// <summary>
        
/// 构造 AuthenticatedAttribute 类型的新实例。
        
/// </summary>

        public AuthenticatedAttribute()
        
{
            isAuthenticated 
= true;
        }


        
/// <summary>
        
/// 构造 AuthenticatedAttribute 类型的新实例。
        
/// </summary>
        
/// <param name="isAuthenticated">该类是否自动验证。true为是。</param>

        public AuthenticatedAttribute(bool isAuthenticated)
        
{
            
this.isAuthenticated = isAuthenticated;
        }


        
public bool IsAuthenticated
        
{
            
get
            
{
                
return isAuthenticated;
            }

        }


    }

}

 

 

做个页面的基类。他来实现权限验证。

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.Security;
using System.Web.Configuration;

using CE.Components.Base.Security;

namespace CE.Components.Base
{
    
public class PageBase : Page
    
{

        
private UserInfoBase userInfo;

        
public PageBase()
        
{
            
//用户登录检查。
            Authenticated();

        }


        
/// <summary>
        
/// 获取当前用户信息。
        
/// </summary>

        protected UserInfoBase UserInfo
        
{
            
get
            
{
                
return userInfo;
            }

        }


        
private void Authenticated()
        
{
            AuthenticatedAttribute attribute;
            
string userInfoProviderName;

            
//检查当前页面是否标记自动验证登录的属性。
            foreach (Attribute attr in Page.GetType().GetCustomAttributes(true))
            
{

                attribute 
= attr as AuthenticatedAttribute;

                
//如果标记了。
                if (attribute != null)
                
{

                    
if (attribute.IsAuthenticated)
                    
{
                        
//如果当前页面需要自动验证登录,并且应用程序使用forms认证。
                        if (SystemWebConfig.AuthenticationMode == AuthenticationMode.Forms)
                        
{

                            
//如果没有登录
                            if (!User.Identity.IsAuthenticated)
                            
{

                                
//自动跳转到登录页。
                                FormsAuthentication.RedirectToLoginPage();

                            }

                            
else //如果登录了,加载用户信息。
                            {
                                
//UserInfoBase userInfo = new UserInfoBase();
                                userInfoProviderName =
                                    WebConfigurationManager.AppSettings[
"UserInfoProviderName"];

                                
//如果设置用户信息提供类型。
                                if (!string.IsNullOrEmpty(userInfoProviderName))
                                
{
                                    userInfo 
= ProviderFactory.LoadProvider
                                        (userInfoProviderName) 
as UserInfoBase;
                                }

                            }

                        }

                    }


                    
break;
                }

            }

        }


    }

}

 

 这个类其实很简单,但不得不提一下ProviderFactory。

这里需要配下webconfig的appsetting节点。

<add key="UserInfoProviderName" value="UserInfoProvider" />

可以参考http://www.cnblogs.com/ttcre2/archive/2008/07/28/1254671.html

他是微软做的,我就只封装了下,我觉得挺好的,起码比一般的反射要好。

 

 

基类都做完了,然后看下调用吧。

首先是用户信息。

using System;
using System.Collections.Generic;
using System.Text;

namespace CE.Components.Base.Security
{
    
public class UserInfo : UserInfoBase
    
{

        
public UserInfo()
        
{
           
//this.Data 是直接从cookie里面读出来的。
            
//你可以在登录的时候把些常用的信息序列化成字符串存到data中。
            
//像部门,职位,员工ID,邮箱等等。
            
//然后在这里得到data,反序列化这个字符串,还原成属性。
            
//当然,你也可以只记个id,然后在这里自己去数据库查。很灵活,随便你。
        }


    }

}

 

 

然后是登录方法

protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
{

    
string userData;

    
//先到数据库中去验证。顺便取得需要放入cookie的信息。

    
//然后登录。
    userData = "用户信息,序列化后的字符串";
    UserInfoBase.RedirectFromLoginPage(Login1.UserName, Login1.RememberMeSet, userData);
}

 

最后做个需要做权限验证页面的基类继承PageBase,给这个基类加上特性Authenticated(true)。那么只要集成了这个类的页面就会自动验证用户和加载用户信息了。未登录的直接跳到登录页,登录后返回原来页面。多方便啊。

 

总结:

以上方法只需要给页面加上一个特性后,页面就会自动具备身份验证功能。

身份验证成功后,自动加载用户信息。

用户信息格式完全由开发人员自定义。

加这篇已经是第三篇页面基类的文章了,以后还会陆续加。我这里还有好多,都是平时工作中碰到的问题。有个好的框架,开发起来但速度快,更重要的是质量。

posted on 2008-07-28 13:21  猫咬狗  阅读(4068)  评论(29编辑  收藏  举报