• XAF自定义登录参数和身份验证


      在使用XAF框架是,默认身份验证是使用用户名和密码的。Win和Web页面如下所示。

      现在要(公司、员工、密码)登录来替代(用户名、密码)登录。效果如下:

      原理

      是如何实现自定义传参验证的呢?最关键的步骤在于登录使用自定义的认证标准(能够实现登录)而不是默认的Authentication Standard。

      我们可以先了解在生成项目时,有无登录页面的区别。分别打开生成简单的无自动登录和有自动登录的默认Win应用配置(Web应用配置类似)文件WinApplication.cs。

      因此,默认登录需要两个XAF控件Security Strategy Complex 和 Authentication Standard,字面意思是复杂安全策略和认证标准。

      Security Strategy Complex,复杂安全策略。对其功能简单的理解就是定义登录的角色类型、用户类型等信息。查看Security Strategy Complex的属性。实现登录控件是角色类型(RoleType)和用户类型(UserType)来实现自定义的用户登录。

       Authentication Standard, 认证标准。简单的理解,就是根据复杂安全策略提供的参数,有了这个控件,就能实现登录。它的方法本身定义了登录页面需要登录的参数。要想实现登录页面,就需要自己写一个控件,然后替换Authentication Standard控件

       注:记得将自定义的控件替换为Authentication Standard(开始时依据官方步骤,不理解原理,遗漏此步骤,因此百思不得其解)。

      如何自定义控件呢?Authentication Standard控件对应的类为AuthenticationStandard,我们可以自定义类,官方的方法是继承AuthenticationBase、或者IAuthenticationStandard接口。下图是登录类的继承关系图,蓝色部分为登录控件,可以查看官方文件(https://documentation.devexpress.com/eXpressAppFramework/DevExpress.ExpressApp.Security.AuthenticationStandard.class)根据需求,实现和集成。接下来的方法会按照官方的方法来做。

    步骤

    要实现自定义登录(公司、员工、密码)登录。依据以下步骤:

    实现员工公司类,并生成演示数据

    • 创建参数类
    • 创建自定义登录认证类
    • 让登录窗口下拉能显示数据
      • 为登录窗口提供对象空间
      • 让登录窗口能够访问外部数据
    • 将自定义类传递给安全系统

    实现员工公司类,并生成演示数据

      为了实现登录,员工(Employee)需要继承PermissionPolicyUser类。公司(Company)与员工需要创建关联(association)。

      员工(Employee)具体代码如下:

     1 using DevExpress.Xpo;
     2 using DevExpress.ExpressApp.DC;
     3 using DevExpress.Persistent.Base;
     4 using DevExpress.Persistent.BaseImpl.PermissionPolicy;
     5 //……
     6     [DefaultClassOptions]
     7     [XafDefaultProperty("UserName"),XafDisplayName("员工")]
     8     public class Employee : PermissionPolicyUser
     9     {
    10         public Employee(Session session) : base(session) { }
    11         private Company company;
    12         [Association("Company-Employees")]
    13         [XafDisplayName("公司")]
    14         public Company Company
    15         {
    16             get { return company; }
    17             set { SetPropertyValue("Company", ref company, value); }
    18         }
    19 }
    20 //……

      公司(Company)类具体代码:

    using DevExpress.Xpo;
    using DevExpress.ExpressApp.DC;
    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.BaseImpl;
    //……
        [DefaultClassOptions]
        [XafDisplayName("公司"),XafDefaultProperty("Name")]
        public class Company : BaseObject
        {
            public Company(Session session) : base(session) { }
            private string name;
            
            [XafDisplayName("名称")]
            public string Name
            {
                get { return name; }
                set { SetPropertyValue("Name", ref name, value); }
            }
            [Association("Company-Employees")]
            public XPCollection<Employee> Employees
            {
                get { return GetCollection<Employee>("Employees"); }
            }
        }
    //……

      接下来调用项目的模型编辑器,展开Views | CustomLogonParametersExample.Module.BusinessObjects节点,并找到员工(Employee)和公司(Company)的列表视图(ListView视图),将属性AllowNew设置为false。为了防止在这两个视图中创建新对象。

       为了方便验证,打开 BatabaseUpdate→Update.cs文件,添加验证数据。

     1 using DevExpress.ExpressApp.Security;
     2 using DevExpress.ExpressApp.Security.Strategy;
     3 // ... 
     4 public class Updater : ModuleUpdater {
     5     public Updater(IObjectSpace objectSpace, Version currentDBVersion) : base(objectSpace, currentDBVersion) { }
     6     public override void UpdateDatabaseAfterUpdateSchema() {
     7         base.UpdateDatabaseAfterUpdateSchema();
     8         PermissionPolicyRole administrativeRole = ObjectSpace.FindObject<PermissionPolicyRole>(
     9             new BinaryOperator("Name", SecurityStrategy.AdministratorRoleName));
    10         if (administrativeRole == null) {
    11             administrativeRole = ObjectSpace.CreateObject<PermissionPolicyRole>();
    12             administrativeRole.Name = SecurityStrategy.AdministratorRoleName;
    13             administrativeRole.IsAdministrative = true;
    14         }
    15         const string adminName = "Administrator";
    16         Employee administratorUser = ObjectSpace.FindObject<Employee>(
    17             new BinaryOperator("UserName", adminName));
    18         if (administratorUser == null) {
    19             administratorUser = ObjectSpace.CreateObject<Employee>();
    20             administratorUser.UserName = adminName;
    21             administratorUser.IsActive = true;
    22             administratorUser.SetPassword("");
    23             administratorUser.Roles.Add(administrativeRole);
    24         }
    25         PermissionPolicyRole userRole = ObjectSpace.FindObject<PermissionPolicyRole>(
    26             new BinaryOperator("Name", "User"));
    27         if (userRole == null) {
    28             userRole = ObjectSpace.CreateObject<PermissionPolicyRole>();
    29             userRole.Name = "User";
    30             userRole.AddTypePermission<Employee>(
    31                 SecurityOperations.ReadOnlyAccess, SecurityPermissionState.Allow);
    32             userRole.AddTypePermission<Company>(
    33                 SecurityOperations.ReadOnlyAccess, SecurityPermissionState.Allow);
    34         }
    35         if (ObjectSpace.FindObject<Company>(null) == null) {
    36             Company company1 = ObjectSpace.CreateObject<Company>();
    37             company1.Name = "Company 1";
    38             company1.Employees.Add(administratorUser);
    39             Employee user1 = ObjectSpace.CreateObject<Employee>();
    40             user1.UserName = "Sam";
    41             user1.SetPassword("");
    42             user1.Roles.Add(userRole);
    43             Employee user2 = ObjectSpace.CreateObject<Employee>();
    44             user2.UserName = "John";
    45             user2.SetPassword("");
    46             user2.Roles.Add(userRole);
    47             Company company2 = ObjectSpace.CreateObject<Company>();
    48             company2.Name = "Company 2";
    49             company2.Employees.Add(user1);
    50             company2.Employees.Add(user2);
    51         }
    52         ObjectSpace.CommitChanges();
    53     }
    54 }

     创建参数类

      在项目*.Modeule下添加参数类CustomLogonParameters。代码如下。

     1 using System;
     2 using System.ComponentModel;
     3 using System.Runtime.Serialization;
     4 using DevExpress.ExpressApp.DC;
     5 using DevExpress.Persistent.Base;
     6 using UserTest.Module.BusinessObjects;
     7 
     8 //……
     9    [DomainComponent, Serializable]
    10     [System.ComponentModel.DisplayName("登录")]                       //登录标题
    11     public class CustomLogonParameters : INotifyPropertyChanged, ISerializable
    12     {
    13         #region 构造方法
    14         public CustomLogonParameters() { }
    15 
    16         //ISerializable,序列化
    17         public CustomLogonParameters(SerializationInfo info, StreamingContext context)
    18         {
    19             if (info.MemberCount > 0)
    20             {
    21                 UserName = info.GetString("UserName");
    22                 Password = info.GetString("Password");
    23             }
    24         }
    25         #endregion
    26         
    27         private Company company;                //公司
    28         private Employee employee;              //员工
    29         private string password;                //密码
    30 
    31         [ImmediatePostData]   //值应该尽快传递给绑定对象,但用户更改值时,允许强制更新
    32         [XafDisplayName("公司")]
    33         public Company Company
    34         {
    35             get { return company; }
    36             set
    37             {
    38                 if (value == company) return;
    39                 company = value;
    40                 Employee = null;
    41                 OnPropertyChanged("Company");
    42             }
    43         }
    44         [DataSourceProperty("Company.Employees")]
    45         [ImmediatePostData] //值应该尽快传递给绑定对象,但用户更改值时,允许强制更新
    46         [XafDisplayName("员工")]
    47         public Employee Employee
    48         {
    49             get { return employee; }
    50             set
    51             {
    52                 if (Company == null) return;
    53                 employee = value;
    54                 if (employee != null)
    55                 {
    56                     UserName = employee.UserName;
    57                 }
    58                 OnPropertyChanged("Employee");
    59             }
    60         }
    61         [Browsable(false)]                  //用户名在登录页面不显示    
    62         [XafDisplayName("用户名")]                 
    63         public String UserName { get; set; }
    64         [PasswordPropertyText(true)]  
    65         [XafDisplayName("密码")]
    66         public string Password
    67         {
    68             get { return password; }
    69             set
    70             {
    71                 if (password == value){ return; }
    72                 password = value;
    73             }
    74         }
    75 
    76         public event PropertyChangedEventHandler PropertyChanged;
    77         //属性更改时引发事件页面上能快速更改事件)
    78         private void OnPropertyChanged(string propertyName)
    79         {
    80             if (PropertyChanged != null)
    81             {
    82                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    83             }
    84         }       
    85 
    86         [System.Security.SecurityCritical]
    87         public void GetObjectData(SerializationInfo info, StreamingContext context)
    88         {
    89             info.AddValue("UserName", UserName);
    90             info.AddValue("Password", Password);
    91         }
    92 }
    93 //……

      注:在登录验证的时候,不能使用引用属性验证。因此,在方法中,根据公司、员工获取用户名(UserName),登录验证的时候使用用户名+密码验证。这就是为什么参数中需要加一个因此用户名的原因。

    创建自定义登录认证类

      创建类CustomAuthentication。为了实现自定义身份认证,需要继承AuthenticationBase。具体代码如下:

     1 using System;
     2 using DevExpress.ExpressApp;
     3 using DevExpress.Data.Filtering;
     4 using System.Collections.Generic;
     5 using DevExpress.ExpressApp.Security;
     6 using UserTest.Module.BusinessObjects;
     7 //……
     8 
     9 /// <summary>
    10     /// 自定义登录参数类
    11     /// </summary>
    12     public class CustomAuthentication : AuthenticationBase, IAuthenticationStandard
    13     {
    14         private CustomLogonParameters customLogonParameters;            //登录参数
    15         public CustomAuthentication()
    16         {
    17             customLogonParameters = new CustomLogonParameters();
    18         }
    19         /// <summary>
    20         /// 注销
    21         /// </summary>
    22         public override void Logoff()
    23         {
    24             base.Logoff();
    25             customLogonParameters = new CustomLogonParameters();
    26         }
    27         /// <summary>
    28         /// 重置登录参数
    29         /// </summary>
    30         public override void ClearSecuredLogonParameters()
    31         {
    32             customLogonParameters.Password = "";
    33             base.ClearSecuredLogonParameters();
    34         }
    35         /// <summary>
    36         /// 通过比较登录参数与数据库中用户对象的值,进行验证。
    37         /// 
    38         /// </summary>
    39         /// <param name="objectSpace">用户数据操作对象空间</param>
    40         /// <returns>经身份验证的用户</returns>
    41         public override object Authenticate(IObjectSpace objectSpace)
    42         {
    43 
    44             Employee employee = objectSpace.FindObject<Employee>(new BinaryOperator("UserName", customLogonParameters.UserName));
    45             if (employee == null)
    46             {
    47                 throw new ArgumentNullException("Employee");
    48             }
    49             if (!employee.ComparePassword(customLogonParameters.Password))
    50             {
    51                 throw new AuthenticationException(employee.UserName, "密码错误.");
    52             }
    53             return employee;
    54         }
    55 
    56         /// <summary>
    57         /// 初始化登录参数
    58         /// </summary>
    59         /// <param name="logonParameters">登录参数</param>
    60         public override void SetLogonParameters(object logonParameters)
    61         {
    62             this.customLogonParameters = (CustomLogonParameters)logonParameters;
    63         }
    64 
    65         /// <summary>
    66         /// 返回到要添加程序的应用列表
    67         /// </summary>
    68         /// <returns></returns>
    69         public override IList<Type> GetBusinessClasses()
    70         {
    71             return new Type[] { typeof(CustomLogonParameters) };
    72         }
    73         /// <summary>
    74         /// 指示登录过程是否是交互式的(通过登录对话框请求登录参数)
    75         /// </summary>
    76         public override bool AskLogonParametersViaUI
    77         {
    78             get { return true; }
    79         }
    80 
    81         /// <summary>
    82         /// 返回登录参数
    83         /// </summary>
    84         public override object LogonParameters
    85         {
    86             get { return customLogonParameters; }
    87         }
    88         /// <summary>
    89         /// 是否启用注销方法,继承AuthenticationBase
    90         /// </summary>
    91         public override bool IsLogoffEnabled
    92         {
    93             get { return true; }
    94         }
    95 }
    96 
    97 //……

       注:记得初始化方法LogonParameters。

    让登录窗口下拉能显示数据

      完成参数类和自定义登录认证类后,直接将自定义类传递给安全系统(后一步会介绍)。会发现下拉框无法获取到数据。

      要使得下拉框能够获取到这些外部数据。我们需要为登录窗口提供对象空间和让登录窗口访问外部数据。

      以Win版本为例,提供对象空间后,下拉时能够获取到数据,数据处于数据保护。

       让窗口访问外部数据(公司类型)以后,下拉时能够读取数据。

    为登录窗口提供对象空间

    Win版

      打开Program.cs ,修改代码如下:

     1 using CustomLogonParametersExample.Module;
     2 using CustomLogonParametersExample.Module.BusinessObjects;
     3 using DevExpress.ExpressApp;
     4 // ... 
     5 static class Program {
     6     // ... 
     7     static void Main() {
     8         // ... 
     9         winApplication.CreateCustomLogonWindowObjectSpace += 
    10         application_CreateCustomLogonWindowObjectSpace;
    11         winApplication.Setup();
    12         // ... 
    13     }
    14     private static void application_CreateCustomLogonWindowObjectSpace(object sender, 
    15     CreateCustomLogonWindowObjectSpaceEventArgs e) {
    16         e.ObjectSpace = ((XafApplication)sender).CreateObjectSpace(typeof(CustomLogonParameters));
    17         if (e.ObjectSpace is NonPersistentObjectSpace) {
    18             IObjectSpace objectSpaceCompany = ((XafApplication)sender).CreateObjectSpace(typeof(Company));
    19             ((NonPersistentObjectSpace)e.ObjectSpace).AdditionalObjectSpaces.Add(objectSpaceCompany);
    20         }
    21     }
    22 }

       打开WinApplication.cs,修改代码如下:

     1 using DevExpress.ExpressApp;
     2 using DevExpress.ExpressApp.Win;
     3 using DevExpress.ExpressApp.Xpo;
     4 // ... 
     5 public partial class CustomLogonParametersExampleWindowsFormsApplication : WinApplication {
     6     // ... 
     7     protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
     8         args.ObjectSpaceProviders.Add(new XPObjectSpaceProvider(args.ConnectionString, args.Connection, false));
     9         args.ObjectSpaceProviders.Add(new NonPersistentObjectSpaceProvider(TypesInfo, null));
    10     }
    11 }

     Web版

      Web代码如下:

     1 using CustomLogonParametersExample.Module;
     2 using CustomLogonParametersExample.Module.BusinessObjects;
     3 using DevExpress.ExpressApp;
     4 using DevExpress.ExpressApp.Web;
     5 // ... 
     6 public class Global : System.Web.HttpApplication {
     7     // ... 
     8     protected void Session_Start(Object sender, EventArgs e) {
     9         // ... 
    10         WebApplication.Instance.CreateCustomLogonWindowObjectSpace += 
    11         application_CreateCustomLogonWindowObjectSpace;
    12         WebApplication.Instance.Setup();
    13         // ... 
    14     }
    15     private static void application_CreateCustomLogonWindowObjectSpace(object sender, 
    16     CreateCustomLogonWindowObjectSpaceEventArgs e) {
    17         e.ObjectSpace = ((XafApplication)sender).CreateObjectSpace(typeof(CustomLogonParameters));
    18         if (e.ObjectSpace is NonPersistentObjectSpace) {
    19             IObjectSpace objectSpaceCompany = ((XafApplication)sender).CreateObjectSpace(typeof(Company));
    20             ((NonPersistentObjectSpace)e.ObjectSpace).AdditionalObjectSpaces.Add(objectSpaceCompany);
    21         }
    22     }
    23 }
     1 using System;
     2 using System.Collections.Generic;
     3 using CustomLogonParametersExample.Module;
     4 using DevExpress.ExpressApp;
     5 using DevExpress.ExpressApp.DC;
     6 using DevExpress.ExpressApp.Xpo;
     7 using DevExpress.ExpressApp.Web;
     8 // ... 
     9 public partial class CustomLogonParametersExampleAspNetApplication : WebApplication {
    10     private static NonPersistentTypeInfoSource nonPersistentTypeInfoSource;
    11     // ... 
    12     protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
    13         args.ObjectSpaceProvider = 
    14         new XPObjectSpaceProvider(GetDataStoreProvider(args.ConnectionString, args.Connection), true);
    15         if (nonPersistentTypeInfoSource == null) {
    16             nonPersistentTypeInfoSource = new NonPersistentTypeInfoSource(TypesInfo, new List<Type>() 
    17                { typeof(CustomLogonParameters) });
    18         }
    19         args.ObjectSpaceProviders.Add(new NonPersistentObjectSpaceProvider(TypesInfo, 
    20         nonPersistentTypeInfoSource));
    21     }
    22 }

     让登录窗口能够访问外部数据

      为了能够让下拉框读取公司、员工数据。

      Win版本,打开WinApplication.cs,代码如下。

    using DevExpress.ExpressApp;
    using DevExpress.ExpressApp.Win;
    using DevExpress.ExpressApp.Security;
    //……
    public partial class UserTestWindowsFormsApplication : WinApplication {
    //……
    public UserTestWindowsFormsApplication() {
    //……
    //允许在登录窗口中访问公司、员工数据
                ((SecurityStrategy)Security).AnonymousAllowedTypes.Add(typeof(Company)); 
              ((SecurityStrategy)Security).AnonymousAllowedTypes.Add(typeof(Employee));
    }
    //……
    }
    //……

      Web版本,打开WebApplication.cs,代码如下。

     1 using DevExpress.ExpressApp;
     2 using DevExpress.ExpressApp.Security;
     3 using DevExpress.ExpressApp.Web;
     4 // ... 
     5 public partial class UserTestAspNetApplication: WebApplication {
     6     // ... 
     7     public UserTestAspNetApplication () {
     8         // ... 
     9 //允许登录串口查看公司、员工数据
    10             ((SecurityStrategy)Security).AnonymousAllowedTypes.Add(typeof(Company));
    11             ((SecurityStrategy)Security).AnonymousAllowedTypes.Add(typeof(Employee));
    12     }
    13     // ... 
    14 }

     将自定义类传递给安全系统

      调用应用程序设计器。将SecurityStrategyComplex组件从工具箱拖到设计器的安全窗格上。然后,将您在与平台无关的模块中实现的CustomAuthentication组件放在同一个窗格中。

      关注SecurityStrategyComplex组件。在Properties窗口中,设置SecurityStrategy。UserType员工。

     个人源码地址

      百度网盘链接: https://pan.baidu.com/s/1atleFNZeqYqoiNtKfeErIA 提取码: 9fkf

    参考网址

      [1]https://documentation.devexpress.com/eXpressAppFramework/112982/Task-Based-Help/Security/How-to-Use-Custom-Logon-Parameters-and-Authentication

  • 相关阅读:
    MySQL必知必会(数据分组,Group by和Having子句, Select子句的顺序)
    MySQL必知必会(汇总数据, 聚集函数)
    MySQL必知必会(使用函数处理数据)
    菜根谭#206
    菜根谭#205
    菜根谭#204
    菜根谭#203
    菜根谭#202
    菜根谭#201
    菜根谭#200
  • 原文地址:https://www.cnblogs.com/luyj00436/p/11447161.html
Copyright © 2020-2023  润新知