本文实例源代码,可以通过下面地址下载
http://files.cnblogs.com/chenxizhang/SilverlightRIAAuthenticationSample.rar
1. 创建项目,并添加一个业务用的Domain Service
作为演示,我们这里写了一个简单的方法namespace SilverlightRIAAuthenticationSample.Web { using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.ServiceModel.DomainServices.Hosting; using System.ServiceModel.DomainServices.Server; using System.Runtime.Serialization; // TODO: Create methods containing your application logic. [EnableClientAccess()] public class HomeDomainService : DomainService { [Query] ///
写好之后,分别编译网站项目和Silverlight项目。在Silverlight中应该可以看到一个自动生成的类型
2. 编写客户端代码
我简单地做了一个界面,用来显示由服务器返回的客户列表同时,编写一些简单的后台代码(直接写在xaml.cs中)
using System.Windows; using System.Windows.Controls; using SilverlightRIAAuthenticationSample.Web; namespace SilverlightRIAAuthenticationSample { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); Loaded += new RoutedEventHandler(MainPage_Loaded); } void MainPage_Loaded(object sender, RoutedEventArgs e) { var ctx = new HomeDomainContext(); var op=ctx.Load
运行起来,我们可以看到如下的一个效果
到这里为止,我们就已经实现了一个简单的Silverlight+RIA Service的场景。这不是本文的重点,我们下面要实现的是,在这个设计的基础上添加身份验证的功能。
例如你可以假设一下:假如这个GetCustomers方法,并不是给所有用户都可以调用的,而是需要经过身份验证的用户才可以调用的
3. 修改Web.config
我们需要修改宿主网站的web.config,设置身份验证提供程序,可以选择Forms或者Windows,我们这里选择Forms,就是所谓的表单验证,客户端需要提供一个用户名和密码来进行验证4. 添加一个AuthenticationDomainService
在网站项目中这个Domain Service不需要做任何修改。但也可以为User类型添加一些特殊的属性(称为Profile Property),这里先不展开了
但是,这里需要添加一个身份验证的提供程序。我写了一个最简单的MemberShipProvider
请注意,SimpleMembershipProvider,只实现一个方法:ValidateUser(请注意代码的底部,红色部分)
using System; using System.ServiceModel.DomainServices.Hosting; using System.ServiceModel.DomainServices.Server.ApplicationServices; namespace SilverlightRIAAuthenticationSample.Web { [EnableClientAccess] public class AuthenticationDomainService : AuthenticationBase
然后,我们还需要修改web.config,指定这个MembershipProvider,请注意下面的粗体部分
6. 如何在服务器端启用身份验证
请注意下面代码的修改,在GetCustomers方法上面,我们添加一个RequiresAuthetication的Attribute。namespace SilverlightRIAAuthenticationSample.Web { using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel.DomainServices.Hosting; using System.ServiceModel.DomainServices.Server; // TODO: Create methods containing your application logic. [EnableClientAccess()] public class HomeDomainService : DomainService { [Query][RequiresAuthentication] ///
到这里为止,服务器端的设计就完成了,我们可以将两个项目都重新生成一下
然后,按下F5,重新运行一下Silverlight程序,不出意外的话,我们会看到下面一个错误
如果想要看到错误消息,可以对代码稍作修改
using System.Windows; using System.Windows.Controls; using SilverlightRIAAuthenticationSample.Web; namespace SilverlightRIAAuthenticationSample { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); Loaded += new RoutedEventHandler(MainPage_Loaded); } void MainPage_Loaded(object sender, RoutedEventArgs e) { var ctx = new HomeDomainContext(); var op = ctx.Load
再次运行的话,就可以看到如下的消息提供
事情很清楚了,因为服务端要求要做身份验证,而客户端没有提供有关的用户信息,所以就报告了如上的错误
7. 如何在客户端中使用身份验证
有很多个做法可以实现客户端的身份验证,通常是用一个窗口让用户输入用户名和密码。为了简单起见,我先用最简单的方法来实现。我们可以修改App.xaml.cs文件
using System; using System.ServiceModel.DomainServices.Client.ApplicationServices; using System.Windows; namespace SilverlightRIAAuthenticationSample { public partial class App : Application { public App() { this.Startup += this.Application_Startup; this.Exit += this.Application_Exit; this.UnhandledException += this.Application_UnhandledException; InitializeComponent(); //创建一个上下文,这是在RIA Service中的一个对象 var webcontext = new WebContext(); webcontext.Authentication = new FormsAuthentication(); this.ApplicationLifetimeObjects.Add(webcontext); } private void Application_Startup(object sender, StartupEventArgs e) { //直接使用硬编码的方式进行登录,注意这里是异步的 var param = new LoginParameters("chenxizhang", "password"); WebContext.Current.Authentication.Login(param, (op) => { if(op.User != null && op.User.Identity.IsAuthenticated) this.RootVisual = new MainPage(); else { MessageBox.Show("Login fail, please try again. "); } }, null); } private void Application_Exit(object sender, EventArgs e) { } private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) { // If the app is running outside of the debugger then report the exception using // the browser's exception mechanism. _disibledevent=>// icon in the status bar and Firefox will display a script error. if(!System.Diagnostics.Debugger.IsAttached) { // NOTE: This will allow the application to continue running after an exception has been thrown // but not handled. // For production applications this error handling should be replaced with something that will // report the error to the website and stop the application. e.Handled = true; Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); }); } } private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e) { try { string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace; errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n"); System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");"); } catch(Exception) { } } } }
然后,再运行程序,我们就可以看到查询结果了
这就表示,我们已经登录成功了,所有后续代码就生效了。
除了上面那种用代码方式在App类型的构造器实例化身份验证方式之外,还可以通过下面这样修改xaml来实现
【备注】关于ApplicationLifetimeObjects,这是WPF和Silverlight的一个特殊功能,可以将一个对象在整个应用程序生命周期里面都有效,相当于是全局的对象。有兴趣可以参考这里的说明
8. 实现用户登录窗口
接下来,我们实现一个简单的用户登录窗口吧。假设我们希望任何用户在使用这个程序之前,必须首先要登录后台代码如下
using System.ServiceModel.DomainServices.Client.ApplicationServices; using System.Windows; using System.Windows.Controls; namespace SilverlightRIAAuthenticationSample { public partial class LoginWindow : ChildWindow { public LoginWindow() { InitializeComponent(); } private void OKButton_Click(object sender, RoutedEventArgs e) { //直接使用硬编码的方式进行登录,注意这里是异步的 var param = new LoginParameters(txtUserName.Text,txtPassword.Text); WebContext.Current.Authentication.Login(param, (op) => { if(op.User != null && op.User.Identity.IsAuthenticated) { (App.Current.RootVisual as ContentControl).Content = new MainPage(); this.DialogResult = true; } else { MessageBox.Show("Login fail, please try again. "); this.DialogResult = null; } }, null); } private void CancelButton_Click(object sender, RoutedEventArgs e) { this.DialogResult = false; } } }
接下来,我们需要对App.xaml.cs做点修改
using System; using System.ServiceModel.DomainServices.Client.ApplicationServices; using System.Windows; using System.Windows.Controls; namespace SilverlightRIAAuthenticationSample { public partial class App : Application { public App() { this.Startup += this.Application_Startup; this.Exit += this.Application_Exit; this.UnhandledException += this.Application_UnhandledException; InitializeComponent(); } private void Application_Startup(object sender, StartupEventArgs e) { //使用用户登录对话框进行登录 var content = new ContentControl() { VerticalContentAlignment = System.Windows.VerticalAlignment.Stretch, HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch }; content.Content = new TextBlock() { Text = "Please login first", FontSize = 50 }; this.RootVisual = content; var form = new LoginWindow(); form.Show(); } private void Application_Exit(object sender, EventArgs e) { } private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) { // If the app is running outside of the debugger then report the exception using // the browser's exception mechanism. _disibledevent=>// icon in the status bar and Firefox will display a script error. if(!System.Diagnostics.Debugger.IsAttached) { // NOTE: This will allow the application to continue running after an exception has been thrown // but not handled. // For production applications this error handling should be replaced with something that will // report the error to the website and stop the application. e.Handled = true; Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); }); } } private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e) { try { string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace; errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n"); System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");"); } catch(Exception) { } } } }
这两段代码的意思是,先启动LoginWindow,然后根据用户输入的信息进行登录,成功的话,就显示主窗口,调试起来看的效果如下
输入用户名和密码,例如
然后点击“Ok”,因为我提供的用户是合法的,所以会显示MainPage
如果我提供的用户信息不合法呢?
一点都不意外,我们将收到一个提示
9.实现用户自动登录
假设我们希望这个程序能够实现用户的自动登录,也就是说不要每次都输入用户名和密码,该怎么办呢?private void Application_Startup(object sender, StartupEventArgs e) { ///用LoadUser方法可以自动将保存在本地的用户凭据去做身份验证 WebContext.Current.Authentication.LoadUser((result) => { if(result.User != null && result.User.Identity.IsAuthenticated) {//如果成功的话,就直接显示MainPage this.RootVisual = new MainPage(); } else { //使用用户登录对话框进行登录 var content = new ContentControl() { VerticalContentAlignment = System.Windows.VerticalAlignment.Stretch, HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch }; content.Content = new TextBlock() { Text = "Please login first", FontSize = 50 }; this.RootVisual = content; var form = new LoginWindow(); form.Show(); } }, null); }
同时,我们还需要修改LoginWindow,让他可以保存用户凭据(你是否记得我们在那个窗口上有一个复选框呢?),请注意下面红色的部分
private void OKButton_Click(object sender, RoutedEventArgs e) { //直接使用硬编码的方式进行登录,注意这里是异步的 var param = new LoginParameters( txtUserName.Text, txtPassword.Text, (bool)chkRemember.IsChecked, string.Empty); WebContext.Current.Authentication.Login(param, (op) => { if(op.User != null && op.User.Identity.IsAuthenticated) { (App.Current.RootVisual as ContentControl).Content = new MainPage(); this.DialogResult = true; } else { MessageBox.Show("Login fail, please try again. "); this.DialogResult = null; } }, null); }
这样修改完之后,重新运行项目,第一次的时候,因为本地没有保存凭据,所以我们会被要求进行登录
请注意,我们选中“Remember me”,然后点击“Ok”
然后,我们可以关掉浏览器,重新启动程序。因为之前保存了用户凭据,所以你将直接可以看到MainPage,而无需登录
很不错,对吧?最后遗留了一个问题,就是如果我想用其他用户登录的话,怎么办呢?我肯定希望将原先保存好的用户凭据删除掉。如何实现这样的功能呢?
10. 实现用户注销
为了实现注销,我们一般在主窗口上面,添加一个HyperLinkButton,如上所示。它的代码也很简单
private void btLogout_Click(object sender, RoutedEventArgs e) { WebContext.Current.Authentication.Logout(true); }
11.总结
本文,我用一个实例讲解了如何在RIA Service中启用身份验证。这是很实用的技术,我们使用到了Authentication Domain Service,和自定义的MembershipProvider,在客户端我们还实现了登录窗口以及自动登录。本文实例源代码,可以通过下面地址下载
http://files.cnblogs.com/chenxizhang/SilverlightRIAAuthenticationSample.rar
最新评论