ria身份验证,如何在RIA Service中启用身份验证

本文我将结合一个实例,一步一步地演示,然后在RIA Service中启用身份验证。包括在服务端的设计,和客户端的设计。
本文实例源代码,可以通过下面地址下载
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] /// /// 这个方法返回一些客户名称 /// /// public IQueryable GetCustomers() { return new[]{ new Customer(){ID=1,Name="Microsoft"}, new Customer(){ID=2,Name="Google"}, new Customer(){ID=3,Name="Apple"}, new Customer(){ID=4,Name="Facebook"}, new Customer(){ID=5,Name="Yahoo"}, new Customer(){ID=16,Name="AOL"} }.AsQueryable(); } } [DataContract] public class Customer { [Key][DataMember] public int ID { get; set; } [DataMember] public string Name { get; set; } } }
写好之后,分别编译网站项目和Silverlight项目。在Silverlight中应该可以看到一个自动生成的类型
image如何在RIA Service中启用身份验证ria身份验证

2. 编写客户端代码

我简单地做了一个界面,用来显示由服务器返回的客户列表
imageimage如何在RIA Service中启用身份验证ria身份验证


同时,编写一些简单的后台代码(直接写在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(ctx.GetCustomersQuery()); this.DataContext = op.Entities; } } }
运行起来,我们可以看到如下的一个效果
imageimageimage如何在RIA Service中启用身份验证ria身份验证
到这里为止,我们就已经实现了一个简单的Silverlight+RIA Service的场景。这不是本文的重点,我们下面要实现的是,在这个设计的基础上添加身份验证的功能。
例如你可以假设一下:假如这个GetCustomers方法,并不是给所有用户都可以调用的,而是需要经过身份验证的用户才可以调用的

3. 修改Web.config

我们需要修改宿主网站的web.config,设置身份验证提供程序,可以选择Forms或者Windows,我们这里选择Forms,就是所谓的表单验证,客户端需要提供一个用户名和密码来进行验证
imageimageimageimage如何在RIA Service中启用身份验证ria身份验证

4. 添加一个AuthenticationDomainService

在网站项目中
imageimageimageimageimage如何在RIA Service中启用身份验证ria身份验证
这个Domain Service不需要做任何修改。但也可以为User类型添加一些特殊的属性(称为Profile Property),这里先不展开了
imageimageimageimageimageimage如何在RIA Service中启用身份验证ria身份验证
但是,这里需要添加一个身份验证的提供程序。我写了一个最简单的MemberShipProvider
请注意,SimpleMembershipProvider,只实现一个方法:ValidateUser(请注意代码的底部,红色部分)
using System; using System.ServiceModel.DomainServices.Hosting; using System.ServiceModel.DomainServices.Server.ApplicationServices; namespace SilverlightRIAAuthenticationSample.Web { [EnableClientAccess] public class AuthenticationDomainService : AuthenticationBase { // To enable Forms/Windows Authentication for the Web Application, edit the appropriate section of web.config file. } public class User : UserBase { // NOTE: Profile properties can be added here // To enable profiles, edit the appropriate section of web.config file. // public string MyProfileProperty { get; set; } } public class SimpleMembershipProvider : System.Web.Security.MembershipProvider { public override string ApplicationName { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override bool ChangePassword(string username, string oldPassword, string newPassword) { throw new NotImplementedException(); } public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { throw new NotImplementedException(); } public override System.Web.Security.MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out System.Web.Security.MembershipCreateStatus status) { throw new NotImplementedException(); } public override bool DeleteUser(string username, bool deleteAllRelatedData) { throw new NotImplementedException(); } public override bool EnablePasswordReset { get { throw new NotImplementedException(); } } public override bool EnablePasswordRetrieval { get { throw new NotImplementedException(); } } public override System.Web.Security.MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); } public override System.Web.Security.MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); } public override System.Web.Security.MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); } public override int GetNumberOfUsersOnline() { throw new NotImplementedException(); } public override string GetPassword(string username, string answer) { throw new NotImplementedException(); } public override System.Web.Security.MembershipUser GetUser(string username, bool userIsOnline) { throw new NotImplementedException(); } public override System.Web.Security.MembershipUser GetUser(object providerUserKey, bool userIsOnline) { throw new NotImplementedException(); } public override string GetUserNameByEmail(string email) { throw new NotImplementedException(); } public override int MaxInvalidPasswordAttempts { get { throw new NotImplementedException(); } } public override int MinRequiredNonAlphanumericCharacters { get { throw new NotImplementedException(); } } public override int MinRequiredPasswordLength { get { throw new NotImplementedException(); } } public override int PasswordAttemptWindow { get { throw new NotImplementedException(); } } public override System.Web.Security.MembershipPasswordFormat PasswordFormat { get { throw new NotImplementedException(); } } public override string PasswordStrengthRegularExpression { get { throw new NotImplementedException(); } } public override bool RequiresQuestionAndAnswer { get { throw new NotImplementedException(); } } public override bool RequiresUniqueEmail { get { throw new NotImplementedException(); } } public override string ResetPassword(string username, string answer) { throw new NotImplementedException(); } public override bool UnlockUser(string userName) { throw new NotImplementedException(); } public override void UpdateUser(System.Web.Security.MembershipUser user) { throw new NotImplementedException(); } public override bool ValidateUser(string username, string password) { return username == "chenxizhang" && password == "password"; } } }
然后,我们还需要修改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] /// /// 这个方法返回一些客户名称 /// /// public IQueryable GetCustomers() { return new[]{ new Customer(){ID=1,Name="Microsoft"}, new Customer(){ID=2,Name="Google"}, new Customer(){ID=3,Name="Apple"}, new Customer(){ID=4,Name="Facebook"}, new Customer(){ID=5,Name="Yahoo"}, new Customer(){ID=16,Name="AOL"} }.AsQueryable(); } } [DataContract] public class Customer { [Key][DataMember] public int ID { get; set; } [DataMember] public string Name { get; set; } } }
到这里为止,服务器端的设计就完成了,我们可以将两个项目都重新生成一下
然后,按下F5,重新运行一下Silverlight程序,不出意外的话,我们会看到下面一个错误
imageimageimageimageimageimageimage如何在RIA Service中启用身份验证ria身份验证
如果想要看到错误消息,可以对代码稍作修改
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(ctx.GetCustomersQuery(), (result) => { if(result.HasError) { MessageBox.Show(result.Error.Message); result.MarkErrorAsHandled(); } }, true); this.DataContext = op.Entities; } } }
再次运行的话,就可以看到如下的消息提供
imageimageimageimageimageimageimageimage如何在RIA Service中启用身份验证ria身份验证
事情很清楚了,因为服务端要求要做身份验证,而客户端没有提供有关的用户信息,所以就报告了如上的错误

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) { } } } }
然后,再运行程序,我们就可以看到查询结果了
imageimageimageimageimageimageimageimageimage如何在RIA Service中启用身份验证ria身份验证
这就表示,我们已经登录成功了,所有后续代码就生效了。
除了上面那种用代码方式在App类型的构造器实例化身份验证方式之外,还可以通过下面这样修改xaml来实现
【备注】关于ApplicationLifetimeObjects,这是WPF和Silverlight的一个特殊功能,可以将一个对象在整个应用程序生命周期里面都有效,相当于是全局的对象。有兴趣可以参考这里的说明

8. 实现用户登录窗口

接下来,我们实现一个简单的用户登录窗口吧。假设我们希望任何用户在使用这个程序之前,必须首先要登录
imageimageimageimageimageimageimageimageimageimage如何在RIA Service中启用身份验证ria身份验证

最新评论

发表评论