WPF 和客户端应用程序服务的注销问题
我最近正在使用 WPF 和客户端应用程序服务对应用程序进行原型设计。我正在尝试使用客户端应用程序服务的所有三个功能:表单身份验证、角色安全性和客户端配置文件。我已经设法让一切正常工作,但我觉得我的解决方案是一个拼凑,想要一个更好的方法。
为了进行讨论,假设如下:
我有两个 WPF 窗口:
第一个窗口是我的主应用程序表单。它有两个按钮“登录”和“检查线程主体”。
第二个窗口是一个登录表单,它实现 IClientFormsAuthenticationCredentialsProvider 接口,以使用 GetCredentials 方法返回带有输入的用户名和密码的新 ClientFormsAuthenticationCredentials 对象。
应用程序启动,加载主窗口,用户单击登录按钮。在按钮单击事件中,将调用 System.Web.Security.Membership.ValidateUser(String.Empty, String.Empty)。这会导致调用我的登录表单中的 GetCredentials 方法,从而显示登录窗口。主窗口按钮单击事件中的代码等待用户在登录窗口中输入信息。
一旦用户提交登录信息,登录表单关闭,执行流程返回到主窗口登录单击事件。如果提交的用户名和密码有效,则 Thread.CurrentPrincipal 具有适当的 FormAuthentication 主体。
如果我们返回主窗口窗体并单击“检查线程主体”按钮,然后检查按钮单击事件上的 Thread.CurrentPrincipal,我们会看到 Thread.CurrentPrincipal 不再是 FormsAuthentication 主体,而是通用 Windows主要的。
在我的研究中,我找到了几种处理此问题的方法:
第一种是在验证用户后立即调用 AppDomain.CurrentDomain.SetThreadPrincipal(Thread.CurrentPrincipal)。这将确保 FormsAuthentication 主体在应用程序的整个生命周期中一直存在。问题是我不能设置两次。因此,如果我调用 ClientFormsAuthenticationMembershipProvider.LogOut() 方法,并尝试再次执行登录过程。我收到“默认主体对象不能设置两次”的错误。
第二种方法,也是我为原型选择的方法,是创建一个名为 UserAuthentication 的类。此类中有一个名为 Current 的静态属性,它返回 UserAuthentication 类型的单例。在 UserAuthentication 类中,我维护自己的 CurrentPrinipal 和 Original Primary 属性,它们的类型为 IPrincipal。我还有 LogIn 和 LogOut 方法,用于管理对 System.Web.Security.Membership.ValidateUser(String.Empty, String.Empty) 和 ClientFormsAuthenticationMembershipProvider.LogOut() 的调用,并维护封装的 CurrentPrinipal 和 Original Primary 属性的状态。
通过作为单例执行此操作,我仍然可以通过调用 CommonAssembly.UserAuthentication.Current.CurrentPrincipal.IsInRole("SomeRole") 来访问 IsInRole 功能。我可以通过确保在调用 Properties.Settings.Default.Save() 之前调用 System.Threading.Thread.CurrentPrincipal = CommonAssembly.UserAuthentication.Current.CurrentPrincipal 来实现客户端配置文件功能。
这是可行的,但我觉得这是一个拼凑,并且在我的研究中我无法找到适当的解决方案。我知道部分问题在于 WPF 如何处理执行上下文,但我觉得必须有更好的方法。
我希望我的解释和内联代码示例不会太令人困惑。实现所有这些所需的代码超出了我在帖子中所能写的范围。
供参考。我在堆栈溢出上看到过类似的线程,但没有一个真正充分回答了这个问题。我还在网络上看到过客户端应用程序服务实现示例,但没有一个专门使用非 silverlight WPF。
那么是否有更好的方法允许我使用 Thread.CurrentPrincipal 进行登录/注销,从而避免我维护自己的委托人?
我感谢任何人可以提供的任何帮助或见解。
亲切的问候,
伯尼
I have recently been prototyping an application using WPF and Client Application Services. I am trying to use all three features of the Client Application Services : Forms Authenticiation, Role Security, and Client Profile. I have managed to get everything working, but I feel like my solution is a kludge and want a better way.
For the discussion assume the following:
I have two WPF windows:
The first window is my main Application form. It has two buttons "Login" and "Check Thread Principal"
The second window is a login form which implements the IClientFormsAuthenticationCredentialsProvider interface to use the GetCredentials method to return a new ClientFormsAuthenticationCredentials object with the entered username and password.
The application starts, the Main Window is loaded and the user clicks the login button. In the button click event, a call is made to System.Web.Security.Membership.ValidateUser(String.Empty, String.Empty). This causes the GetCredentials method in my login form to be called which in turns shows the login window. The code in the button click event of the main window waits until the user enters information in the login window.
Once the user submits the login information, the login form closes, the flow of execution is returned to the main window login click event. If the username and password submitted were valid, the Thread.CurrentPrincipal has the appropriate FormAuthentication Principal.
If we go back to the main window form and click the "Check Thread Principal" button, and check the Thread.CurrentPrincipal on the button click event, we see that the Thread.CurrentPrincipal is no longer the FormsAuthentication Principal, but instead a Generic Windows Principal.
In my research, I've found a couple of ways to handle this:
The first is to call AppDomain.CurrentDomain.SetThreadPrincipal(Thread.CurrentPrincipal) just after the user is validated. This will ensure that the FormsAuthentication Principal sticks around for the lifetime of the application. The problem with this is that I cannot set it twice. So if I call the ClientFormsAuthenticationMembershipProvider.LogOut() method, and try to do the login process again. I get an "Default principal object cannot be set twice" execption.
The second method, and the method I chose for my prototype is to create a class called UserAuthentication. In this class is a static property called Current which returns a singleton of type UserAuthentication. In the UserAuthentication class I maintain my own CurrentPrinipal and Original Principal properties which are of type IPrincipal. I also have LogIn and LogOut methods which manage the calls to System.Web.Security.Membership.ValidateUser(String.Empty, String.Empty) and ClientFormsAuthenticationMembershipProvider.LogOut() and maintain the state of my encapsulated CurrentPrinipal and Original Principal properties.
By doing this as a singleton, I can still access the IsInRole functionality by calling CommonAssembly.UserAuthentication.Current.CurrentPrincipal.IsInRole("SomeRole"). And I can implement the client profile feature by ensuring that just before I call Properties.Settings.Default.Save(), I call System.Threading.Thread.CurrentPrincipal = CommonAssembly.UserAuthentication.Current.CurrentPrincipal.
This works, but I feel like it is a kludge, and in my research I have not been able to find an adequate solution. I understand that part of the problem is how WPF handles execution context, but I feel like there MUST be a better way.
I hope my explaination and inline code examples aren't too confusing. The code required to implement all of this is more than I can put in a post.
FYI. I have seen similar threads on stack overflow, but none really adequately answer the question. I have also seen client application services implementation examples on the web, but none specifically using non-silverlight WPF.
So is there a better method that allows me to use Thread.CurrentPrincipal for login/logout which obviates the need for me to maintain my own Principal?
I appreciate any help or insight that anyone can provide.
Kind Regards,
Bernie
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论