WCF,ASP.NET成员资格提供程序和身份验证服务
我编写了与WCF服务(BasicHttpBinding)通信的Silverlight 2应用程序。托管Silverlight内容的站点使用ASP.NET成员资格提供程序进行保护。我可以从WCF服务中使用HttpContext.Current.User.Identity.Name访问当前用户,并且我已打开AspNetCompatibilityRequirementsMode。
我现在想使用完全相同的Web服务编写Windows应用程序。为了处理身份验证,我已经启用了身份验证服务,并且可以调用"登录"来验证我的用户... Okey,一切都很好...但是,我到底该如何在其他服务客户端上设置该身份验证cookie?
两种服务都托管在同一个域中
- MyDataService.svc <-处理我的数据的那个
- AuthenticationService.svc <-Windows应用程序必须调用以进行身份验证的那个。
我不想为Windows客户端创建新服务,也不想使用其他绑定...
客户端应用程序服务是另一种选择,但是所有示例都仅限于显示如何获取用户,角色和他的个人资料...但是,一旦我们使用客户端应用程序服务进行了身份验证,就应该有一种获取身份验证Cookie的方法回拨到同一服务器时,连接到我的服务客户端。
根据同事的意见,该解决方案正在添加一个wsHttpBinding端点,但我希望可以解决这个问题。
解决方案
回答
Web服务(例如由WCF创建的服务)通常最好以"无状态"方式使用,因此对Web服务的每次调用都重新开始。这简化了服务器代码,因为不需要进行"会话"来调用客户端的状态。由于不需要保留票证,Cookie或者其他假设某些有关服务器状态的geegaws,因此它也简化了客户端代码。
以描述的方式创建两个服务会引入状态。客户端是"已认证"或者"未认证"的,而MyDataService.svc必须确定哪个。
碰巧的是,当成员资格提供程序用于验证对服务的每次调用时,我发现WCF可以很好地工作。因此,在给出的示例中,我们想要将成员资格提供程序身份验证gubbins添加到MyDataService的服务配置中,而根本没有单独的身份验证服务。
有关详细信息,请参见此处的MSDN文章。
[我很懒,对此我最有吸引力的是,这完全是声明性的。我只是在应用程序的app.config中分散了我的MembershipProvider的正确配置条目,并且!答对了!对服务中每个合同的所有调用都经过身份验证。]
可以公平地注意到,这并不是很快。如果将SQL Server用于身份验证数据库,则每个服务调用将至少有一个(也许有两个)存储过程调用。在许多情况下(尤其是HTTP绑定),服务调用本身的开销会更大;如果不是,请考虑使用我们自己的缓存身份验证请求的成员资格提供程序的实现。
这没有提供的一件事就是提供"登录"功能的能力。为此,我们可以提供不执行任何操作(已认证!)的服务合同(如果身份验证失败,则会引发故障),或者可以使用原始引用的文章中所述的成员资格提供程序服务。
回答
我终于找到了一种完成这项工作的方法。对于身份验证,我使用的是" WCF身份验证服务"。验证服务时,将尝试设置验证cookie。我需要从响应中获取此Cookie,并将其添加到对同一台计算机上的其他Web服务的其他任何请求中。这样做的代码如下所示:
var authService = new AuthService.AuthenticationServiceClient(); var diveService = new DiveLogService.DiveLogServiceClient(); string cookieHeader = ""; using (OperationContextScope scope = new OperationContextScope(authService.InnerChannel)) { HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(); OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty; bool isGood = authService.Login("jonas", "jonas", string.Empty, true); MessageProperties properties = OperationContext.Current.IncomingMessageProperties; HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)properties[HttpResponseMessageProperty.Name]; cookieHeader = responseProperty.Headers[HttpResponseHeader.SetCookie]; } using (OperationContextScope scope = new OperationContextScope(diveService.InnerChannel)) { HttpRequestMessageProperty httpRequest = new HttpRequestMessageProperty(); OperationContext.Current.OutgoingMessageProperties.Add(HttpRequestMessageProperty.Name, httpRequest); httpRequest.Headers.Add(HttpRequestHeader.Cookie, cookieHeader); var res = diveService.GetDives(); }
如我们所见,我有两个服务客户端,一个用于身份验证服务,一个用于我将实际使用的服务。第一个块将调用Login方法,并从响应中获取身份验证cookie。第二个块将在调用" GetDives"服务方法之前将标头添加到请求中。
我对这个代码一点都不满意,我认为一个更好的选择可能是使用" Web参考"代替"服务参考",而改用.NET 2.0堆栈。
回答
可以将许多额外的代码隐藏在自定义消息检查器和行为的后面,因此我们无需自己动手操作OperationContextScope。
稍后,我将尝试模拟某些东西并将其发送给我们。
--larsw
回答
我们应该看看System.Net中的CookieContainer对象。此对象允许非浏览器客户端挂接到cookie。这是我们团队上次遇到该问题时使用的方法。
这是一篇有关如何使用它的简短文章。那里可能会有更好的选择,但这应该可以入门。
我们为当前的WCF服务和Silverlight 2应用程序采用了无状态路由。尽管Silverlight 2需要一些自定义的安全代码,但仍可以使Silverlight 2与受TransportWithMessageCredential安全性绑定的服务一起使用。结果是任何应用程序都可以简单地通过在消息头中设置用户名和密码来访问服务。可以在自定义IRequestChannel实现中完成一次,因此开发人员无需担心自己设置值。尽管WCF确实为开发人员提供了一种简便的方法,但我相信这是serviceProxy.Security.Username和serviceProxy.Security.Password或者同样简单的方法。
回答
我是在使用Client Application Services进行Web服务身份验证时写的。它使用消息检查器插入cookie标头。有一个带文档和演示项目的Word文件。尽管它与我们所做的不完全相同,但它非常接近。你可以在这里下载。
回答
在客户端上,将服务的<binding>标记(在<system.serviceModel>内部)修改为包括:allowCookies =" true"
该应用程序现在应该保留cookie并使用它。我们会注意到,IsLoggedIn现在在登录后返回true-如果我们不允许cookie,则返回false。