13

我编写了一个与 WCF 服务 (BasicHttpBinding) 通信的 Silverlight 2 应用程序。托管 Silverlight 内容的站点使用 ASP.NET 成员资格提供程序进行保护。我可以使用 WCF 服务中的 HttpContext.Current.User.Identity.Name 访问当前用户,并且我已经打开了 AspNetCompatibilityRequirementsMode。

我现在想使用完全相同的 Web 服务编写一个 Windows 应用程序。为了处理身份验证,我启用了身份验证服务,并且可以调用“登录”来对我的用户进行身份验证......好吧,一切都很好......但是我怎么能在我的其他服务客户端上设置那个身份验证 cookie?!

两种服务都托管在同一个域上

  • MyDataService.svc <- 处理我的数据的那个
  • AuthenticationService.svc <- Windows 应用程序必须调用以进行身份​​验证的那个。

我不想为 Windows 客户端创建新服务,或者使用其他绑定...

客户端应用程序服务是另一种选择,但所有示例都仅限于展示如何获取用户、角色和他的个人资料......但是一旦我们使用客户端应用程序服务进行身份验证,就应该有一种方法来获取该身份验证 cookie回调到同一服务器时附加到我的服务客户端。

根据同事的意见,解决方案是添加一个 wsHttpBinding 端点,但我希望我能解决这个问题......

4

6 回答 6

5

我终于找到了一种方法来完成这项工作。对于身份验证,我使用“ 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 Reference”而不是“Service Reference”并使用 .NET 2.0 堆栈。

于 2008-09-11T21:59:47.800 回答
2

Web 服务(例如由 WCF 创建的那些)通常最好以“无状态”方式使用,因此对 Web 服务的每次调用都重新开始。这简化了服务器代码,因为不需要调用客户端状态的“会话”。它还简化了客户端代码,因为不需要保留票证、cookie 或其他假设服务器状态的东西。

以所描述的方式创建两个服务会引入有状态。客户端要么是“经过身份验证”,要么是“未经过身份验证”,MyDataService.svc 必须找出哪个。

碰巧的是,当成员资格提供程序用于对服务的每次调用进行身份验证时,我发现 WCF 运行良好。因此,在给出的示例中,您希望将成员资格提供程序身份验证 gubbins 添加到 MyDataService 的服务配置中,并且根本没有单独的身份验证服务。

有关详细信息,请参阅此处的 MSDN 文章。

[这对我来说非常有吸引力,因为我很懒,这完全是声明性的。我只是将我的 MembershipProvider 的正确配置条目分散在应用程序的 app.config 中,并且!答对了!对服务中每个合约的所有调用都经过身份验证。]

公平地说,这不会特别快。如果您将 SQL Server 用于身份验证数据库,那么每个服务调用至少会有一个,也许是两个存储过程调用。在许多情况下(特别是对于 HTTP 绑定)服务调用本身的开销会更大;如果没有,请考虑滚动您自己的缓存身份验证请求的成员资格提供程序的实现。

没有提供的一件事是提供“登录”功能的能力。为此,您可以提供一个(经过身份验证的!)服务合约,它什么都不做(除了在身份验证失败时引发错误),或者您可以使用原始引用文章中描述的成员资格提供程序服务。

于 2008-09-11T20:21:41.233 回答
1

在客户端上修改服务的 <binding> 标记(在 <system.serviceModel> 内)以包含:allowCookies="true"

应用程序现在应该保留 cookie 并使用它。您会注意到 IsLoggedIn 现在在您登录后返回 true —— 如果您不允许使用 cookie,它会返回 false。

于 2009-07-09T04:15:16.783 回答
0

可以将许多额外代码隐藏在自定义消息检查器和行为后面,因此您无需自己修补 OperationContextScope。

稍后我将尝试模拟某些内容并将其发送给您。

--larsw

于 2008-09-15T09:51:03.493 回答
0

您应该看一下 System.Net 中的CookieContainer对象。此对象允许非浏览器客户端挂在 cookie 上。这是我的团队上次遇到该问题时使用的方法。

这是一篇关于如何使用它的简短文章。那里可能有更好的,但这应该让你开始。

我们为当前的一组 WCF 服务和 Silverlight 2 应用程序采用了无状态路线。可以让 Silverlight 2 与绑定了 TransportWithMessageCredential 安全性的服务一起使用,尽管它在 Silverlight 端需要一些自定义安全代码。结果是任何应用程序都可以通过在消息头中设置用户名和密码来访问服务。这可以在自定义 IRequestChannel 实现中完成一次,因此开发人员无需担心自己设置值。尽管 WCF 确实为开发人员提供了一种简单的方法来做到这一点,我相信它是 serviceProxy.Security.Username 和 serviceProxy.Security.Password 或同样简单的东西。

于 2009-01-16T18:45:23.210 回答
0

当我使用客户端应用程序服务对 Web 服务进行身份验证时,我写了这个。它使用消息检查器插入 cookie 标头。有一个带有文档和演示项目的 word 文件。虽然它不完全是你在做什么,但它非常接近。你可以从这里下载。

于 2009-05-11T20:43:18.353 回答