WCF、ASP.NET Membership Provider和身份验证服务

13

我编写了一个使用基础Http绑定与WCF服务通信的Silverlight 2应用程序。托管Silverlight内容的站点使用ASP.NET会员提供程序进行保护。我可以使用HttpContext.Current.User.Identity.Name从我的WCF服务访问当前用户,并且已经启用了AspNetCompatibilityRequirementsMode。

现在,我想编写一个使用完全相同的Web服务的Windows应用程序。为了处理身份验证,我启用了身份验证服务,并可以调用“login”来验证我的用户......好的,一切顺利......但是我该如何在其他服务客户端上设置认证cookie?!

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

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

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

客户端应用程序服务是另一种选择,但是所有示例都限于显示如何获取用户、角色和其配置文件......但是一旦我们使用客户端应用程序服务进行身份验证,就应该有一种方法将认证cookie附加到在返回到同一服务器时调用的服务客户端上。

根据同事们的建议,解决方案是添加wsHttpBinding端点,但我希望可以避免这样做......

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();
}      

如您所见,我有两个服务客户端,一个用于身份验证服务,另一个用于我要使用的服务。第一个块将调用登录方法,并从响应中获取身份验证cookie。第二个块将在调用“GetDives”服务方法之前向请求添加头信息。
我对这段代码并不满意,我认为更好的选择可能是使用“Web Reference”代替“Service Reference”,并使用.NET 2.0堆栈。

2
Web服务,例如由WCF创建的服务,通常最好以“无状态”的方式使用,因此每次调用Web服务都是全新的。这简化了服务器代码,因为不需要具有回忆客户端状态的“会话”。它还简化了客户端代码,因为不需要持有票据、cookies或其他假设服务器状态的小玩意儿。
按照描述的方式创建两个服务会引入状态性。客户端要么是“已认证”,要么是“未认证”,而MyDataService.svc必须找出哪一个。
恰巧我发现当成员提供程序用于验证每个对服务的调用时,WCF运行良好。因此,在给定的示例中,您需要将成员提供程序身份验证小工具添加到MyDataService的服务配置中,而根本不需要单独的身份验证服务。
有关详细信息,请参阅MSDN文章here
“对我而言,这很有吸引力的一点是,我很懒,因为这完全是声明性的。我只需在应用程序的 app.config 中散布适当的配置条目以配置我的 MembershipProvider,并!哇!对服务中的每个契约的所有调用都将得到身份验证。”
“值得注意的是,这不会特别快。如果您使用 SQL Server 作为身份验证数据库,您将每个服务调用至少一个、也许两个存储过程调用。在许多情况下(尤其是对于 HTTP 绑定),服务调用本身的开销将更大;如果不是,则应考虑自己实现缓存身份验证请求的成员资格提供程序。”
“这个方法没有提供“登录”的功能。为此,您可以提供一个已认证的服务契约,该契约除了在身份验证失败时引发故障之外什么也不做,或者您可以像原始参考文章中描述的那样使用成员资格提供程序服务。”

1
在客户端中修改您的服务的<binding>标记(位于<system.serviceModel>内)以包括:allowCookies="true"
应用程序现在应该持久化Cookie并使用它。您会注意到,如果不允许Cookie,则IsLoggedIn现在返回false,而在登录后返回true。

0

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

这里有一篇简短的文章介绍如何使用它。可能还有更好的文章,但这应该可以让您入门。

对于我们当前的一组WCF服务和Silverlight 2应用程序,我们采用了无状态路线。虽然可以通过在Silverlight端使用自定义安全代码来使Silverlight 2与使用TransportWithMessageCredential安全性绑定的服务配合工作,但这需要一些工作。结果是任何应用程序都可以通过在消息头中设置用户名和密码来访问服务。这可以在自定义IRequestChannel实现中执行一次,以便开发人员永远不必担心自己设置值。尽管WCF确实有一种简单的方法供开发人员执行此操作,我相信它是serviceProxy.Security.Username和serviceProxy.Security.Password或类似的东西。


0

我曾经在使用客户端应用程序服务进行Web服务验证时编写过这个代码。它使用消息检查器插入cookie头文件。文档和演示项目都包含在一个Word文件中。尽管这不是你正在做的事情,但它非常相似。你可以从这里下载。


0

可以通过自定义消息检查器和行为来隐藏大部分额外的代码,这样您就不需要自己处理操作上下文范围的调整。

我稍后会尝试模拟一些东西并发送给您。

--larsw


1
听起来不错,Lars。给我一个好的例子,这样我们就可以“关闭这个家伙”了 ;) 干杯,Jonas。 - Jonas Follesø

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接