当使用Standard Authentication验证类型,默认的登录窗口包含两个编辑框:用户名和密码。而本文讲述怎样自定义登录窗口,窗口包含一个下拉式列表选择company,另一个下拉式列表选择该company的employee,还有一个密码输入框。
自定义登录窗口,有以下两种方法:
a.继承AuthenticationStandardLogonParameters类,添加要在登录窗显示的额外属性。然后,可以用安全系统的LogonParameters属性访问特定的登录参数。然而,验证机制仍只使用用户名和密码。所以,若要对额外属性进行验证,需要继承AuthenticationStandard类并重写Authenticate方法。别忘了在Application Designer中指定自定义的登录参数类:
b.实现一个LogonParameters类(不从AuthenticationStandardLogonParameters类继承),添加额外属性。这种情况下,内置的AuthenticationStandard无法执行验证,所以还必须实现自定义的验证策略:继承AuthenticationBase类。
这里,我们使用第二种方法。
实现Employee和Company类
由于Employee必须支持安全系统,故需要继承BasicUser类。
using System;
using System.Collections.Generic;
using System.Text;
using DevExpress.Persistent.BaseImpl;
using DevExpress.Xpo;
using DevExpress.Persistent.Base;
using DevExpress.ExpressApp.Utils;
using DevExpress.Data.Filtering;
using System.ComponentModel;
namespace AccessDatabaseFromLogonForm.Module {
[DefaultClassOptions(), System.ComponentModel.DefaultProperty("UserName")]
public class Employee : SimpleUser {
private Company company;
public Employee(Session session)
: base(session) {
}
[Association("Company-Employees", typeof(Company))]
public Company Company {
get { return company; }
set {
SetPropertyValue("Company", ref company, value);
}
}
}
}
using System;
using DevExpress.Data.Filtering;
using DevExpress.Xpo;
using DevExpress.ExpressApp;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl;
using DevExpress.Persistent.Validation;
using DevExpress.ExpressApp.Utils;
namespace AccessDatabaseFromLogonForm.Module {
[DefaultClassOptions()]
public class Company : BaseObject {
public Company(Session session)
: base(session) {
}
private string name;
public string Name {
get { return name; }
set {
SetPropertyValue("Name", ref name, value);
}
}
[Association("Company-Employees", typeof(Employee))]
public XPCollection Employees {
get { return GetCollection("Employees"); }
}
}
}
实现MyLogonParameters类
//实现ISupportResetLogonParameters接口,不必重启程序实现登录和注销
//在persistent类中,可以在属性setter中使用SetPropertyValue或OnChanged,但MyLogonParameters不是
//从BaseObject继承而来,故只能实现INotifyPropertyChanged接口
[NonPersistent]
public class MyLogonParameters : INotifyPropertyChanged, ISupportResetLogonParameters {
private IObjectSpace objectSpace;
//为避免在登录窗口新建Employee和Company,需要设置AvailableCompanies和AvailableUsers只读
private ReadOnlyCollection<Company> availableCompanies;
private XPCollection<Employee> availableUsers;
private Company company;
private Employee employee;
private string password;
private void OnPropertyChanged(string propertyName) {
if(PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
//显示的Employee要对于其Company,故为availableUsers添加了一个Criteria
private void RefreshAvailableUsers() {
if(availableUsers == null) {
return;
}
availableUsers.Criteria = new BinaryOperator("Company", Company);
if(employee != null && availableUsers.IndexOf(employee) == -1) {
Employee = null;
}
else {
OnPropertyChanged("Employee");
}
}
[Browsable(false)]
public IObjectSpace ObjectSpace {
get { return objectSpace; }
set { objectSpace = value; }
}
//设置为ReadOnlyCollection,只读集合
[Browsable(false)]
public ReadOnlyCollection<Company> AvailableCompanies {
get {
if (objectSpace == null) {
throw new InvalidOperationException("objectSpace is null");
}
if (availableCompanies == null) {
availableCompanies = new ReadOnlyCollection<Company>(ObjectSpace.GetObjects<Company>(null));
}
return availableCompanies;
}
}
[Browsable(false)]
public XPCollection<Employee> AvailableUsers {
get {
if (availableUsers == null) {
availableUsers = (XPCollection<Employee>)ObjectSpace.GetObjects<Employee>();
//设置availableUsers只读,不允许绑定的control增/删数据
availableUsers.BindingBehavior = CollectionBindingBehavior.AllowNone;
RefreshAvailableUsers();
}
return availableUsers;
}
}
//DataSourceProperty表示Company依赖于于属性AvailableCompanies,非常类似于PersistentAlias特性
//但PersistentAlias是对属性的高级”重命名“,DataSourceProperty是属性和数据绑定的关系
//ImmediatePostData表示当Company数据变化后,立即刷新UI
[DataSourceProperty("AvailableCompanies"), ImmediatePostData]
public Company Company {
get { return company; }
set {
company = value;
RefreshAvailableUsers();
}
}
[DataSourceProperty("AvailableUsers"), ImmediatePostData]
public Employee Employee {
get { return employee; }
set {
employee = value;
OnPropertyChanged("Employee");
}
}
[PasswordPropertyText(true)]
public string Password {
get { return password; }
set { password = value; }
}
public void Reset() {
objectSpace = null;
availableCompanies = null;
availableUsers = null;
company = null;
employee = null;
password = null;
}
public event PropertyChangedEventHandler PropertyChanged;
}
在Program.cs 文件中创建AvailableUsers 和 AvailableCompanies 的ObjectSpaced,并订阅CreateCustomLogonWindowObjectSpace事件
static void Main() {
//...
AccessDatabaseFromLogonFormWindowsFormsApplication application =
new AccessDatabaseFromLogonFormWindowsFormsApplication();
application.CreateCustomLogonWindowObjectSpace +=
new EventHandler<CreateCustomLogonWindowObjectSpaceEventArgs>(
application_CreateCustomLogonWindowObjectSpace);
//...
}
static void application_CreateCustomLogonWindowObjectSpace(object sender,
CreateCustomLogonWindowObjectSpaceEventArgs e) {
e.ObjectSpace = ((XafApplication)sender).CreateObjectSpace();
((MyLogonParameters)e.LogonParameters).ObjectSpace = e.ObjectSpace;
}
实现MyAuthentication类
继承AuthenticationBase类,并重写以下方法:
Authenticate
验证时调用。验证时比对数据库里的信息和logon parameter的值。
ClearSecuredLogonParameters
清除logon parameters,防止在程序里访问它们,这样就隐藏了安全信息。
GetBusinessClasses
返回要添加到Application Model 中的bussiness classese。如果不返回LogonParameters,则不会在登录窗口中显示该视图。
AskLogonParametersViaUI
如果需要显示登录窗口获取用户的logon parameters,则返回true。若从其他地方获取logon parameters,如系统活动文件夹,则返回false。
LogonParameters
返回代表当前logon parameters的对象。
IsLogoffEnabled
是否允许“注销”操作。当前仅在ASP.NET可用。
public class MyAuthentication : AuthenticationBase, IAuthenticationStandard {
private MyLogonParameters logonParameters;
//创建一个LogonParameters对象
public MyAuthentication() {
logonParameters = new MyLogonParameters();
}
//清除登录信息
public override void ClearSecuredLogonParameters() {
logonParameters.Password = "";
base.ClearSecuredLogonParameters();
}
//验证登录,正确则返回该登录用户
public override object Authenticate(DevExpress.ExpressApp.IObjectSpace objectSpace) {
if(logonParameters.Employee == null) {
throw new ArgumentNullException("User");
}
//比对密码
if(!logonParameters.Employee.ComparePassword(logonParameters.Password))
{
throw new AuthenticationException(logonParameters.Employee.UserName, "Wrong password");
}
return objectSpace.GetObject(logonParameters.Employee);
}
public override IList<Type> GetBusinessClasses() {
return new Type[] { typeof(MyLogonParameters) };
}
public override bool AskLogonParametersViaUI {
get {
return true;
}
}
public override object LogonParameters {
get { return logonParameters; }
}
public override bool IsLogoffEnabled {
get { return true; }
}
}
将自定义的这些类传递给系统
在Application Designer 中,只能添加内置的AuthenticationStandard策略,所以只能在代码中添加MyAuthentication类。WinForm工程里的Main方法中添加:
AccessDatabaseFromLogonFormWindowsFormsApplication application =
new AccessDatabaseFromLogonFormWindowsFormsApplication();
application.Security = new SecuritySimple<Employee>(new MyAuthentication());