• 读张子阳的用户验证自定义IPrincipal和IIdentity有感


    asp。net本身的验证是用cook实现的,通过cookie的读取进行登录的验证的检测。

    其核心是 Iprincipal 和 Identity。

    Identity 提供了一个与Name 相关的用户信息

    Iprincipal 提供了一个存储用户信息的票据。

    autenticationType 验证类型 包括 from 登录方法 windows登录方法 和另外一种

    Isauthtication  是否通过验证 

    后台User 是一个当前用户信息存储.

    net安全机制 实现方法有 memberShip静态类方法 与FormsAuthentication方法

    以下为实现步骤

    web.config

    <?xml version="1.0"?>
    <configuration>
        <system.web>
           
        //登录的类型设置 Forms 常用的窗口验证
            <authentication mode="Forms">
                //form验证的设置 过期时间 与登录页
                <forms timeout="600" slidingExpiration="true" loginUrl="~/SignIn.aspx" />
            </authentication>
        </system.web>
        
    //特别目录的权限 限制页面为页面地址 限制目录为目录限制
        <location path="AuthOnly.aspx" >
            <system.web>
                <authorization>
                    //禁止匿名用户查看 ?为匿名 *为所有
                    <deny users="?" />
                </authorization>
            </system.web>
        </location>
    </configuration>  

    创建数据库

    在使用网站的时候 App_Code是存储后台代码的DLL的位置,同时也可以放置强类型数据集,因为是

    即使编译。所以做小网站的时候使用新建网站的网站,网站每次修改代码都进行部分编译。

    如果使用网站项目,是必须预先编译的,好处是启动速度快,因为需要直接全站编译。

    创建强类型数据集  App_Code中

    强类型数据集可以方便的进行验证 与增删改操作

    表名+TableAdaoter 是数据适配器自动实现 表的增删改操作。

    User 是表名

    UserTableAdapter 是表适配器

    fill,GetUserTable 填充,与获取列表的方法

    isValidUser 手动添加的带参数的Sql语句 用于实现特别的功能。

    PS:因为懒得截图,直接用子阳大大的图片进行讲解。原文如下

    http://www.cnblogs.com/JimmyZhang/archive/2008/12/07/1349457.html

    获取用户信息

    userName=User.Identity.Name; //获取用户信息票据

    //通过用户名获得与登录同名的Cookie数据

    HttpCookie authCookie = FormsAuthentication.GetAuthCookie(userName, true);

    //解密cookie数据后获得票据

    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);

    // 根据之前的ticket凭据创建新ticket凭据,然后加入自定义信息
            FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(
                ticket.Version, ticket.Name, ticket.IssueDate,
                ticket.Expiration, ticket.IsPersistent, userData);

    // 将新的Ticke转变为Cookie值,encrypt加密
            authCookie.Value = FormsAuthentication.Encrypt(newTicket);

    //然后添加到Cookies集合中
            HttpContext.Current.Response.Cookies.Add(authCookie);

            // 获得 来到登录页之前的页面,即url中return参数的值
            string url = FormsAuthentication.GetRedirectUrl(userName, true);

    还有一种转向方法FormsAuthentication.????忘记了 有了代码提示就是好,老忘记英文单词。

    注意:这种方法数据单一。如果不想使用memshipUser Role就

    自定义IPrincipal和IIdentity

    我们在App_Code下添加CustomPrincipal和CustomIdentity来实现这两个接口:

    public class CustomPrincipal : IPrincipal {
    
       //私有的当前用户
        private CustomIdentity identity;
    
        //为准备使用identity 赋值  设置值
        public CustomPrincipal(CustomIdentity identity) {
            this.identity = identity;
        }
        //获取值
        public IIdentity Identity {
            get {
                return identity;
            }
        }
    //提供是否 在角色里的验证
        public bool IsInRole(string role) {
            return false;
        }
    }
    
    //类继承IIdentity接口
    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; }
        }
    
    
    //自定义的数据1
        // 这里可以是任意来自数据库的值,由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;
            }
        }
    
    
    //自定义的数据2
    
        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;
    
    // 已经登录 &&登录类型为Forms
        if (user.Identity.IsAuthenticated
            && user.Identity.AuthenticationType == "Forms") {
    
    
    //获取  用户的 identity 信息
            FormsIdentity formIdentity = user.Identity as FormsIdentity;
    
    //获取信息中的票据
            CustomIdentity identity = new CustomIdentity(formIdentity.Ticket);
    
    //通过刚才的类写入到当前用户中
            CustomPrincipal principal = new CustomPrincipal(identity);
    //为当前用户写入
            HttpContext.Current.User = principal;
    //线程操作??????????
            Thread.CurrentPrincipal = principal;
        }
    }

    张子阳大大的话:

    可以看到我们获得了定义在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");
        }
    }

    此类的目的是为了在登录后直接通过siteUser 静态类 获取用户信息的模型。

    protected void Page_Load(object sender, EventArgs e) {
    
    //非返回页 第一次加载
        if (!IsPostBack) {
            if (Request.IsAuthenticated) {
    
      
    //获取用户模型对象
                SiteUser user = SiteUser.GetUser(identity.Name);
    
    //把模型内容设置到控件里
                PopulateControls(user);
    
            }
        }
    }
    private void PopulateControls(SiteUser user) {
        ltrEmail.Text = user.Email;
        ltrName.Text = user.Name;
        ltrPostCount.Text = user.PostCount.ToString();
        ltrRegisterDate.Text = user.RegisterDate.ToShortDateString();
        ltrReplyCount.Text = user.ReplyCount.ToString();
        lnkHomepage.Text = user.HomePage;
        lnkHomepage.NavigateUrl = user.HomePage;
    
        imgHeadImage.ImageUrl = "~/Images/" + user.UserImage;
        imgUserLevel.ImageUrl =
            String.Format("~/Images/Star{0}.gif", user.Level);
    
        pnlUserInfo.Visible = true;
    }
    

      

    其实总结下来

    登录流程

    用户登录

    验证用户 检查用户名与密码 验证通过后

    SetUserDataAndRedirect(userName, userData);

    获取cookie

    HttpCookie authCookie = FormsAuthentication.GetAuthCookie(userName, true);

    获得用户票据

    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);

    使用此方法建立新的票据

    FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket()

    //加密票据

    authCookie.Value = FormsAuthentication.Encrypt(newTicket);

    //写入当前cookie

    HttpContext.Current.Response.Cookies.Add(authCookie);

    转向到信息显示页面,准备获取用户信息

    获取信息流程

    通过  identity.Name 获取当前的用户标示

    创建一个用户信息类模型  通过name 实现在数据库中的读取。

    显示到控件中

  • 相关阅读:
    关于webpack的cdn配置
    谁都能听懂的Redux+Redux-Saga超级傻瓜教程
    记一个react拖动排序中的坑:key
    es6 解构写法:给变量取别名
    C++新型强制类型转换。
    C++ new、delete、namespace关键字。
    C++ 调用C语言、extern "C"、__cplusplus关键字
    C++ 重载函数
    liunx 环境下安装 Eclipse C++
    C++ 内联函数 inline关键字
  • 原文地址:https://www.cnblogs.com/uddgm/p/5450924.html
Copyright © 2020-2023  润新知