尽管在Windows应用程序中做了类似的事情,但我在这方面做得很糟糕.我正在研究WPF应用程序(Prism,Unity,MVVM),我刚刚完成了登录视图.一旦针对SQL Server中的表验证了用户的凭据,我就这样做:
Thread.CurrentPrincipal = user.GenericPrincipal();
Run Code Online (Sandbox Code Playgroud)
用户类定义如下:
public class ApplicationIdentity : GenericIdentity, IApplicationIdentity
{
public string UserName { get; set; }
public bool Authenticated { get; set; }
public ICollection<IModuleProperties> Modules { get; set; }
public ICollection<IViewProperties> Views { get; set; }
public ApplicationIdentity(string userName, IAuthenticatedUser authenticatedUser)
: base(userName)
{
UserName = userName;
Authenticated = authenticatedUser.Authenticated;
Modules = authenticatedUser.Modules;
Views = authenticatedUser.Views;
}
public IPrincipal GenericPrincipal()
{
return new GenericPrincipal(this, null);
}
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是,在登录屏幕被解除后几乎立即我打这个电话:
var currentUser = (IApplicationIdentity)Thread.CurrentPrincipal.Identity;
Run Code Online (Sandbox Code Playgroud)
这引发了一个异常,即Identity无法转换为类型IApplicationIdentity,我不确定我缺少什么.到目前为止,我所阅读的所有SO/Google文章都通过针对AD验证用户来解决了这个问题,但这在我的场景中不起作用.
我试图在这里解决的问题只是坚持当前登录的用户以及他们应该有权访问的模块和视图.如果在设置CurrentPrincipal之外有更好的方法来实现这一点,我对其他解决方案完全开放.感谢您提供的任何帮助!
编辑(解决方案):
我只想关闭解决方案的循环.接下来是一些教程,所以它有点冗长,但应该对任何绊倒它的人有所帮助.接受的答案建议我注入Unity容器,注册我的对象的实例,并从那里使用它.我同意这可能是正确的方法,但它要求我在我的Bootstrapper上"破解"一下.在我开始使用Bootstrapper逻辑之前,我应该考虑一下准备工作.
在我原来的方法中,我的登录视图没有在我的容器中注册,因为登录视图在我的Bootstrapper运行之前被实例化(App.xaml打开了Login视图.)
我做的第一件事是使我的View和ViewModel可注射ala:
public Login(ILoginViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
public LoginViewModel(IUnityContainer container)
{
Container = container;
}
Run Code Online (Sandbox Code Playgroud)
再说一次:对于使用Unity(或一般IoC)的人来说,这应该是熟悉的.然后,一旦用户通过身份验证,我就需要使用Unity注册我当前的用户对象:
private void Login(object obj)
{
...
if (user.Authenticated)
{
Container.RegisterInstance("CurrentUser", user);
}
...
}
Run Code Online (Sandbox Code Playgroud)
任何跟随互联网上任何Prism/Unity/MVVM文章的人都可能熟悉以下方法:
protected override IModuleCatalog CreateModuleCatalog()
{
var catalog = new ModuleCatalog();
catalog.AddModule(typeof (CoreModule));
catalog.AddModule(typeof (CoreModule2));
catalog.AddModule(typeof (CoreModule3));
return catalog;
}
Run Code Online (Sandbox Code Playgroud)
这种方法很简单,但在现实世界的场景中,用户可以访问的模块可能是动态的而不是静态的(或两者的组合.) 之前CreateModuleCatalog()在Bootstrapper Run()方法中调用InitializeShell().在我的情况下,我仍然有一个所有用户都可以访问的静态模块(无论授权级别如何),但(在我看来)从这种方法实例化Login视图会觉得"反模式"(更不用说注册) Unity的类型.)因此我CreateModuleCatalog()成了:
protected override IModuleCatalog CreateModuleCatalog()
{
var catalog = new ModuleCatalog();
catalog.AddModule(typeof(CoreModule));
return catalog;
}
Run Code Online (Sandbox Code Playgroud)
我选择使用我的覆盖InitializeShell()来注册我的登录类型,显示登录视图等.我最终结束了这个实现:
protected override void InitializeShell()
{
base.InitializeShell();
Container.RegisterType(typeof (Login), "LoginView");
Container.RegisterType<ILoginViewModel, LoginViewModel>();
Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
ShowLogOn();
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
Run Code Online (Sandbox Code Playgroud)
ShowLogOn()如果用户最终点击Login视图上的Cancel按钮,将关闭应用程序,所以我可能ShowLogOn()在调用之前放置了所有代码 ,base.InitializeShell()以便不运行不必要的代码.ShowLogOn()看起来完全像你可能期望的那样:
private void ShowLogOn()
{
var login = Container.Resolve<Login>();
var dialogResult = login.ShowDialog();
if (!dialogResult.Value)
{
Application.Current.Shutdown(1);
}
else
{
LoadAuthorizedModules();
Application.Current.MainWindow = null;
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
}
}
Run Code Online (Sandbox Code Playgroud)
如果用户取消我的登录视图,则对话框结果将为false,并且应用程序将关闭.如果他们已成功通过身份验证,我们现在需要加载允许他们查看的模块.这也很简单:
private void LoadAuthorizedModules()
{
var currentUser = Container.Resolve<IApplicationIdentity>("CurrentUser");
foreach (var module in currentUser.Modules)
{
var moduleAssembly = Assembly.Load(module.AssemblyName);
var loadingModule = moduleAssembly.GetType(module.Type);
ModuleCatalog.AddModule(new ModuleInfo
{
ModuleName = loadingModule.Name,
ModuleType = loadingModule.AssemblyQualifiedName
});
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法需要更多解释.首先看看这一行,因为它可能令人困惑:var currentUser = Container.Resolve<IApplicationIdentity>("CurrentUser");注意我没有IApplicationIdentity在我的代码中的任何地方明确注册该类型!然而,当我这样做的时候却隐含地注册了:Container.RegisterInstance("CurrentUser", user);技术上我可以将之前的陈述写成:Container.RegisterInstance<IApplicationIdentity>("CurrentUser", user);但这对我来说是多余的,所以做你觉得最舒服的事情.
Modules属性IApplicationIdentity包含一组对象,这些对象包含有关当前用户有权访问的模块的信息. module.AssemblyName是我的自定义模块类型所在的物理程序集的名称,并且module.Type是模块的类型.
正如您所看到的:一旦通过反射功能加载了类型,我们需要将它添加到Unity Bootstrappers ModuleCatalog属性中,这是直接的.假设一切正常,执行应返回到InitializeShell方法,现在应该启动Shell.
这非常冗长,但我希望有人发现它很有用.此外,我对如何使这个代码"更好"的任何意见感兴趣.谢谢!
如果您想将其存储在线程中,您可以查看这个问题 - /sf/answers/468978961/。但如果您只想访问 IApplicationIdentity。我建议您统一注册该实例。
// IUnityContainer can be injected by unity
IUnityContainer container;
// Register this instance each time now you call for IApplicationIdentity this object will be returned
container.RegisterInstance(typeof (IApplicationIdentity), user.GenericPrincipal());
Run Code Online (Sandbox Code Playgroud)
现在,您可以注入 IApplicationIdentity,并且 Unity 将在您每次需要时实现它。您无需担心线程。
| 归档时间: |
|
| 查看次数: |
1530 次 |
| 最近记录: |