EPR类企业管理系统

在我们现有系统基础上或全新开发,提供定制服务
为您的企业高效快速实施ERP,WMS,MES,CRM管理系统
全面管控物料仓库、销售业务、采购业务、仓库业务
生产过程、质量检验、组织架构、业务报表


定制
QQ:460-3528

开发
QQ群:3360-90194

源码
微信:136-3650-3721

如何:基于现有业务类实施自定义安全系统用户

考虑以下情况。您有一个不安全的XAF应用程序,其业务模型包括Employee业务类。此类公开了诸如个人数据,相关部门和分配的任务之类的信息。启用安全系统时会将用户对象添加到业务模型中,但是登录到您的应用程序的用户雇员。本主题说明如何将“用户”和“员工”合并到一个实体中。为此,将支持Employee类中的几个与安全性相关的接口,结果,安全系统会将Employee类型识别为可能的User类型之一。您将把Employee类型分配给应用程序设计器中的SecurityStrategy.UserType属性。另一个好处是,可以使用CurrentUserId() 函数标准运算符来获取当前Employee的标识符(例如,定义“分配给我的任务”列表视图过滤器)。

提示

DevExpress代码示例数据库(http://www.devexpress.com/example=E4160)中提供了完整的示例项目。

注意
  • 在下面的代码段中,演示了XPO业务类。Entity Framework的类似示例是http://www.devexpress.com/example=T390221
  • 作为本主题中描述的方法的替代,您可以从PermissionPolicyUser继承Employee类。要查看示例,请参考如何:实现自定义安全对象(用户,角色,操作权限)主题。

初始商业模式

从新的XAF解决方案开始。将以下EmployeeTask业务类添加到模块项目中

[DefaultClassOptions]
public class Employee : Person {
    public Employee(Session session)
        : base(session) { }
    [Association("Employee-Task")]
    public XPCollection<EmployeeTask> OwnTasks {
        get { return GetCollection<EmployeeTask>(nameof(OwnTasks)); }
    }
}
[DefaultClassOptions, ImageName("BO_Task")]
public class EmployeeTask : Task {
    public EmployeeTask(Session session)
        : base(session) { }
    private Employee owner;
    [Association("Employee-Task")]
    public Employee Owner {
        get { return owner; }
        set { SetPropertyValue(nameof(Owner), ref owner, value); }
    }
}

支持ISecurityUser界面

为包含Employee类的项目添加对DevExpress.ExpressApp.Security.v 19.2 .dll程序集的引用。用以下代码扩展Employee类。

using DevExpress.ExpressApp.Security;
using DevExpress.Persistent.Validation;
// ...
public class Employee : Person, ISecurityUser {
    // ...
    #region ISecurityUser Members
    private bool isActive = true;
    public bool IsActive {
        get { return isActive; }
        set { SetPropertyValue(nameof(IsActive), ref isActive, value); }
    }
    private string userName = String.Empty;
    [RuleRequiredField("EmployeeUserNameRequired", DefaultContexts.Save)]
    [RuleUniqueValue("EmployeeUserNameIsUnique", DefaultContexts.Save, 
        "The login with the entered user name was already registered within the system.")]
    public string UserName {
        get { return userName; }
        set { SetPropertyValue(nameof(UserName), ref userName, value); }
    }
    #endregion
}

有关此接口及其成员的详细信息,请参考ISecurityUser接口说明。

支持IAuthenticationStandardUser界面

注意

如果您不打算使用AuthenticationStandard身份验证类型,请跳过此部分。

用以下代码扩展Employee类。

using System.ComponentModel;
using DevExpress.ExpressApp.Security;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.Base.Security;
// ...
public class Employee : Person, ISecurityUser, IAuthenticationStandardUser {
    // ...
    #region IAuthenticationStandardUser Members
    private bool changePasswordOnFirstLogon;
    public bool ChangePasswordOnFirstLogon {
        get { return changePasswordOnFirstLogon; }
        set { 
            SetPropertyValue(nameof(ChangePasswordOnFirstLogon), ref changePasswordOnFirstLogon, value);
        }
    }
    private string storedPassword;
    [Browsable(false), Size(SizeAttribute.Unlimited), Persistent, SecurityBrowsable]
    protected string StoredPassword {
        get { return storedPassword; }
        set { storedPassword = value; }
    }
    public bool ComparePassword(string password) {
        return PasswordCryptographer.VerifyHashedPasswordDelegate(this.storedPassword, password);
    }
    public void SetPassword(string password) {
        this.storedPassword = PasswordCryptographer.HashPasswordDelegate(password);
        OnChanged(nameof(StoredPassword));
    }
    #endregion
}

有关此接口及其成员的详细信息,请参考IAuthenticationStandardUser接口说明。

支持IAuthenticationActiveDirectoryUser界面

注意

如果您不打算使用AuthenticationActiveDirectory身份验证类型,请跳过此部分。

IAuthenticationActiveDirectoryUser接口添加到Employee类的受支持接口列表中。

using DevExpress.ExpressApp.Security;
using DevExpress.Persistent.Base.Security;
// ...
public class Employee : Person, ISecurityUser, 
    IAuthenticationStandardUser, IAuthenticationActiveDirectoryUser {
    // ...
}

此接口声明的IAuthenticationActiveDirectoryUser.UserName属性已作为ISecurityUser接口的一部分在您的代码中实现。

支持ISecurityUserWithRoles接口

用以下代码扩展Employee类。

using DevExpress.ExpressApp.Security;
using DevExpress.Persistent.Base.Security;
using DevExpress.Persistent.Validation;
//...
[DefaultClassOptions]
public class Employee : Person, ISecurityUser,
    IAuthenticationStandardUser, IAuthenticationActiveDirectoryUser,
    ISecurityUserWithRoles {
    // ...
    #region ISecurityUserWithRoles Members
    IList<ISecurityRole> ISecurityUserWithRoles.Roles {
        get {
            IList<ISecurityRole> result = new List<ISecurityRole>();
            foreach (EmployeeRole role in EmployeeRoles) {
                result.Add(role);
            }
            return result;
        }
    }
    #endregion
    [Association("Employees-EmployeeRoles")]
    [RuleRequiredField("EmployeeRoleIsRequired", DefaultContexts.Save,
        TargetCriteria = "IsActive",
        CustomMessageTemplate = "An active employee must have at least one role assigned")]
    public XPCollection<EmployeeRole> EmployeeRoles {
        get {
            return GetCollection<EmployeeRole>(nameof(EmployeeRoles));
        }
    }
}

有关此接口及其成员的详细信息,请参考ISecurityUserWithRoles接口说明。

无法定义与内置PermissionPolicyRole类的多对多关联(此类已经与PermissionPolicyUser关联),这就是为什么应在模块项目中实现以下自定义Role类的原因。

using System.Linq;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl.PermissionPolicy;
// ...
[ImageName("BO_Role")]
public class EmployeeRole : PermissionPolicyRoleBase, IPermissionPolicyRoleWithUsers {
    public EmployeeRole(Session session)
        : base(session) {
    }
    [Association("Employees-EmployeeRoles")]
    public XPCollection<Employee> Employees {
        get {
            return GetCollection<Employee>(nameof(Employees));
        }
    }
    IEnumerable<IPermissionPolicyUser> IPermissionPolicyRoleWithUsers.Users {
        get { return Employees.OfType<IPermissionPolicyUser>(); }
    } 
}

支持IPermissionPolicyUser界面

用以下代码扩展Employee类。

using System.Linq;
using DevExpress.ExpressApp.Security;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.Base.Security;
// ...
[DefaultClassOptions]
public class Employee : Person, ISecurityUser,
    IAuthenticationStandardUser, IAuthenticationActiveDirectoryUser,
    IPermissionPolicyUser {
    // ...
    #region IPermissionPolicyUser Members
    IEnumerable<IPermissionPolicyRole> IPermissionPolicyUser.Roles {
        get { return EmployeeRoles.OfType<IPermissionPolicyRole>(); }
    }
    #endregion
}

有关此接口及其成员的详细信息,请参考IPermissionPolicyUser接口说明。

支持ICanInitialize接口

ICanInitialize.Initialize方法用于分配默认角色,当您使用AuthenticationActiveDirectory认证和设置AuthenticationActiveDirectory.CreateUserAutomatically为true。如果不需要支持用户自动创建,请跳过此步骤。否则,使用以下代码扩展Employee类。

using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Security;
using DevExpress.Data.Filtering;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.Base.Security;
// ...
[DefaultClassOptions]
public class Employee : Person, ISecurityUser,
    IAuthenticationStandardUser, IAuthenticationActiveDirectoryUser,
    IPermissionPolicyUser, ICanInitialize {
    // ...
    #region ICanInitialize Members
    void ICanInitialize.Initialize(IObjectSpace objectSpace, SecurityStrategyComplex security) {
        EmployeeRole newUserRole = (EmployeeRole)objectSpace.FindObject<EmployeeRole>(
            new BinaryOperator("Name", security.NewUserRoleName));
        if (newUserRole == null) {
            newUserRole = objectSpace.CreateObject<EmployeeRole>();
            newUserRole.Name = security.NewUserRoleName;
            newUserRole.IsAdministrative = true;
            newUserRole.Employees.Add(this);
        }
    }
    #endregion
}

配置安全系统为了利用自定义用户和角色类型

调用Application Designer,然后将SecurityStrategyComplex组件从“工具箱”拖到“安全性”窗格中。然后,将AuthenticationStandardAuthenticationActiveDirectory组件放在SecurityStrategyComplex附近。要使用自定义EmployeeRole角色和自定义Employee用户而不是默认角色和用户,请在“属性”窗口中修改SecurityStrategyComplex.RoleTypeSecurityStrategy.UserType值。此步骤应在WinForms,ASP.NET和Mobile应用程序中执行。

EmployeeAsUserExample_Designer

创建一个管理帐户

如果决定使用Active Directory身份验证,则可以跳过本节。从标准身份验证开始的最低要求是管理员角色以及与此角色相关联的管理员用户。要添加这些对象,请编辑位于模块项目的DatabaseUpdate文件夹中的Updater.csUpdater.vb)文件。以以下方式重写ModuleUpdater.UpdateDatabaseAfterUpdateSchema方法。

using DevExpress.ExpressApp.Security.Strategy;
// ...
public override void UpdateDatabaseAfterUpdateSchema() {
    base.UpdateDatabaseAfterUpdateSchema();
    EmployeeRole adminEmployeeRole = ObjectSpace.FindObject<EmployeeRole>(
        new BinaryOperator("Name", SecurityStrategy.AdministratorRoleName));
    if (adminEmployeeRole == null) {
        adminEmployeeRole = ObjectSpace.CreateObject<EmployeeRole>();
        adminEmployeeRole.Name = SecurityStrategy.AdministratorRoleName;
        adminEmployeeRole.IsAdministrative = true;
        adminEmployeeRole.Save();
    }
    Employee adminEmployee = ObjectSpace.FindObject<Employee>(
        new BinaryOperator("UserName", "Administrator"));
    if (adminEmployee == null) {
        adminEmployee = ObjectSpace.CreateObject<Employee>();
        adminEmployee.UserName = "Administrator";
        adminEmployee.SetPassword("");
        adminEmployee.EmployeeRoles.Add(adminEmployeeRole);
    }
    ObjectSpace.CommitChanges();
}

运行应用程序

现在,您可以运行Windows窗体或ASP.NET应用程序以查看结果。您将看到Employee对象用作自定义用户类型。

EmployeeAsUserExample_Runtime

筛选属于当前员工的任务

ListViewFilterAttribute属性应用于EmployeeTask类,以定义“列表视图”过滤器。若要引用当前的Employee标识符,请使用CurrentUserId()函数。

using DevExpress.ExpressApp.SystemModule;
// ...
[ListViewFilter("All Tasks", "")]
[ListViewFilter("My Tasks", "[Owner.Oid] = CurrentUserId()")]
public class EmployeeTask : Task {
    // ...
}

下图显示了结果。

EmployeeAsUserExample_RuntimeFilters

转载保留此链接,注明出处