zoukankan      html  css  js  c++  java
  • [转]Asp.Net 用户验证(自定义IPrincipal和IIdentity)

    本文转自:http://www.cnblogs.com/amylis_chen/archive/2012/08/02/2620129.html

    Default.aspx 页面预览
    
    默认情况下SignIn.aspx在登录成功后会导航到Default.aspx页面,所以我们先简单的构建一下Default.aspx页面,看看实现的效果:
    
    <asp:LoginView ID="LoginView1" runat="server">
        <AnonymousTemplate>
            欢迎访问, 游客 !      
        </AnonymousTemplate>
        <LoggedInTemplate>
            你好, <asp:LoginName ID="LoginName1" runat="server" /> ! <br />
            <strong>UserData值:</strong>
            <asp:Literal ID="lbUserData" runat="server" />
        </LoggedInTemplate>
    </asp:LoginView>
    <br />
    <asp:LoginStatus ID="LoginStatus1" runat="server" LogoutPageUrl="~/Logout.aspx" LogoutAction="Redirect" />
    
    类似地,我们放置了一个LoginView控件,只是这里我们多放置了一个LoginStatus控件。接下来我们看一下后置代码:
    
    protected void Page_Load(object sender, EventArgs e) {
    
        if (!IsPostBack) {
            if (Request.IsAuthenticated) {
                FormsIdentity identity = User.Identity as FormsIdentity;
                string userData = identity.Ticket.UserData;
                Literal lbUserData = LoginView1.FindControl("lbUserData") as Literal;
                lbUserData.Text = userData;
            }
        }
    }
    
    最后我们先进行登录,然后再打开Default.aspx页面,会看到类似这样的输出:
    
    
    
    至此,我们已经看到了如何利用FormsAuthentionTicket来附带额外的用户数据,但是我们应该看到这种做法存在的问题:可以保存的数据过于单一,仅仅只是一个字符串。而我们第一节中所介绍的用户表包括各种类型的各种数据。如果你看过了 从一个范例看XML的应用 这篇文章,你应该立刻想到此处又是一个“单一字符串保存多种不同类型数据”的应用场景,我们可以定义XML来解决。对于这种方式,我不再演示了。实际上,我们可以自定义一个IPrincipal和IIdentity来完成,接下来就来看一下。
    
    自定义IPrincipal和IIdentity
    
    不管是在Windows上还是在Web上,.Net都使用这两个接口来实现用户的身份验证。它们不过是一个接口,实现了这两个接口的类型附带了用户的信息,最终被赋予线程(Windows)或Cookie(Web)来对用户进行验证。我们在App_Code下添加CustomPrincipal和CustomIdentity来实现这两个接口:
    
    public class CustomPrincipal : IPrincipal {
    
        private CustomIdentity identity;
    
        public CustomPrincipal(CustomIdentity identity) {
            this.identity = identity;
        }
    
        public IIdentity Identity {
            get {
                return identity;
            }
        }
    
        public bool IsInRole(string role) {
            return false;
        }
    }
    
    public class CustomIdentity : IIdentity {
        private FormsAuthenticationTicket ticket;
        private HttpContext context = HttpContext.Current;
    
        public CustomIdentity(FormsAuthenticationTicket ticket) {
            this.ticket = ticket;
        }
    
        public string AuthenticationType {
            get { return "Custom"; }
        }
    
        public bool IsAuthenticated {
            get { return true; }
        }
    
        public string Name {
            get {
                return ticket.Name;
            }
        }
    
        public FormsAuthenticationTicket Ticket {
            get { return ticket; }
        }
    
        // 这里可以是任意来自数据库的值,由Name属性取得
        // 需要注意此时已通过身份验证
        public string Email {
            get {
                HttpCookie cookie = context.Request.Cookies["Email"];
    
                if (cookie==null || String.IsNullOrEmpty(cookie.Value)) {
                    string type = "jimmy_dev[at]163.com";   // 实际应根据name属性从数据库中获得
                    cookie = new HttpCookie("UserType", type);
                    cookie.Expires = DateTime.Now.AddDays(1);
                    context.Response.Cookies.Add(cookie);
                }
    
                return cookie.Value;
            }
        }
    
        public string HomePage {
            get {
                HttpCookie cookie = context.Request.Cookies["HomePage"];
    
                if (cookie==null || String.IsNullOrEmpty(cookie.Value)) {
                    string name = "www.tracefact.net";      // 实际应根据name属性从数据库中获得
                    cookie = new HttpCookie("NickName", name);
                    cookie.Expires = DateTime.Now.AddDays(1);
                    context.Response.Cookies.Add(cookie);
                }
                return cookie.Value;
            }
        }
    }
    
    注意这里的HomePage和Email这两个属性,它们携带了我们的用户数据,这里我仅仅是对它们进行了一个简单的赋值,实际的数值应该是来自于数据库。还要注意获取到它们的值后被保存在了Cookie中,以避免频繁的对数据库进行访问。
    
    定义了实现这两个接口的对象之后,我们还需要把它嵌入到应用程序的生命周期中,具体的做法就是挂接到HttpModule或者是重写Global.asax中的事件,这里我采用了重写Global.asax事件的方式,因此创建一个Global.asax文件,然后添加如下代码:
    
    void Application_OnPostAuthenticateRequest(object sender, EventArgs e) {
        IPrincipal user = HttpContext.Current.User;
    
        if (user.Identity.IsAuthenticated
            && user.Identity.AuthenticationType == "Forms") {
    
            FormsIdentity formIdentity = user.Identity as FormsIdentity;
            CustomIdentity identity = new CustomIdentity(formIdentity.Ticket);
    
            CustomPrincipal principal = new CustomPrincipal(identity);
            HttpContext.Current.User = principal;
    
            Thread.CurrentPrincipal = principal;
        }
    }
    
    这段代码很好理解,它不过是在应用程序的PostAuthenticateRequest事件中用我们自定义的CustomPrincipal和CustomIdentity替换掉了默认的IPrincipal和IIdentity实现。
    
    Default.aspx页面预览
    
    我们再次对Default.aspx进行修改,添加两个Literal控件,用于显示我们自定义的数值:
    
    自定义Identity中的值:<br />
    <strong>Email:</strong>
    <asp:Literal ID="ltrEmail2" runat="server"></asp:Literal><br />
    
    <strong>HomePage:</strong>
    <asp:Literal ID="ltrHomePage" runat="server"></asp:Literal><br />
    
    然后修改页面的代码,使用我们的自定义CustomIdentity,然后从中获得自定义的属性值:
    
    protected void Page_Load(object sender, EventArgs e) {
    
        if (!IsPostBack) {
            if (Request.IsAuthenticated) {
    
                CustomIdentity identity = User.Identity as CustomIdentity;
                if (identity != null) {
                    // 获得UserData中的值
                    string userData = identity.Ticket.UserData;
                    Literal lbUserData = LoginView1.FindControl("lbUserData") as Literal;
                    lbUserData.Text = userData;
    
                    // 获得identity中的值
                    ltrEmail2.Text = identity.Email;
                    ltrHomePage.Text = identity.HomePage;
                }
            }
        }
    }
    
    如果你现在打开页面,将会看到类似下面的页面:
    
    
    
    可以看到我们获得了定义在CustomIdentity中的属性。注意这里我只是做了一个示范,因此只在CustomIdentity中包含了Email和HomePage两个属性值,如果看到此处你便以为大功告成,然后将所有未完成的属性都添加到CustomIdentity中去就大错特错了。Identity的目的只是为你提供一个已经登录了的用户的名称,而不是携带所有的用户信息,这些信息应该由其他的类型提供。因此微软才定义了MemberShipUser类型和Profile。从这个角度上来看,自定义IPrincipal和IIdentity并没有太大的意义。
    
    这里,我们最好是定义一个自己的类型来承载用户数据,下面我们就看下如何完成。
    
    自定义类型携带用户数据
    
    在App_Code中新建一个SiteUser类,它的实现如下,简单起见,我使用了公有字段而非属性:
    
    public class SiteUser
    {
        public string Name;
        public string UserImage;
        public DateTime RegisterDate;
        public string Email;
        public string HomePage;
        public int PostCount;
        public int ReplyCount;
        public byte Level;
    
        public SiteUser(AuthDataSet.UserRow userRow) {
            this.Email = userRow.Email;
            this.HomePage = userRow.Homepage;
            this.Level = userRow.Level;
            this.Name = userRow.Name;
            this.PostCount = userRow.PostCount;
            this.RegisterDate = userRow.RegisterDate;
            this.ReplyCount = userRow.ReplyCount;
            this.UserImage = userRow.UserImage;
        }
    
        // 实际应该由数据库获得
        public static SiteUser GetUser(string name) {
    
            AuthDataSetTableAdapters.UserTableAdapter adapter
                = new AuthDataSetTableAdapters.UserTableAdapter();
            AuthDataSet.UserDataTable userTable = adapter.GetUserTable(name);
            
            if(userTable.Rows.Count >0){
                return new SiteUser((AuthDataSet.UserRow)userTable.Rows[0]);
            }
    
            // 因为调用这个方法时,name应该是有效的,
            // 如果name无效,直接抛出异常
            throw new ApplicationException("User Not Found");
        }
    }
    
    它的GetUser()静态方法根据用户的名称获得了一个SiteUser对象,这里需要注意的是通常调用这个方法时,用户已经登录过了,也就是说其name参数总是有效的,因此当搜索数据库找不到记录时,我简单地抛出了异常。
    
    
    
  • 相关阅读:
    CCF-CSP201512-3 画图
    CCF-CSP201512-2 消除类游戏
    CCF-CSP201606-4 游戏(BFS)
    CCF-CSP201604-2 俄罗斯方块
    HDU1035 Robot Motion(dfs)
    Java Srting之Calendar日历类(五)——Calendar中计算时间的方法add()
    java如何获取当前日期和时间
    double 类型怎样不用科学计数法表示并且使用Java正则表达式去掉Double类型的数据后面多余的0
    @SpringBootApplication(exclude={DataSourceAutoConfiguration.class})注解作用
    java.util.Date.toString()方法实例
  • 原文地址:https://www.cnblogs.com/freeliver54/p/6258680.html
Copyright © 2011-2022 走看看