WCF安全实现设计问题

时间:2012-05-22 19:41:47

标签: c# .net wcf wcf-security

我有创建ASP.net/WinForms应用程序的经验,我想通过创建一个简单的任务管理器项目来学习WCF。

提前感谢您阅读传入的文本块。我的问题更多的是设计而不是实际的编码问题。

我的目标如下:

  • 创建一个Web服务(使用WCF),其目的是管理任务/待办事项列表
  • Web服务将允许用户注册帐户,创建新待办事项列表,与其他用户共享待办事项列表等。
  • 在Web服务正常运行并且所有内容都已实现之后,我希望能够在其上层叠ASP.NET网站并使用Web服务作为后端

目前我有以下内容:

  • 1个托管Web服务的控制台应用程序
  • 1个用于调用Web服务的控制台应用程序(客户端)(我用这种方式测试我的Web服务)

Web Service应用程序具有以下配置文件(希望我将其粘贴好):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Tasker_Server.Properties.Settings.TaskerConnectionString"
      connectionString="Data Source=PROPHET\SQLEXPRESS;Initial Catalog=Tasker;Persist     Security Info=True;User ID=sa;Password=stf"
  providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.serviceModel>
    <services>
      <service name="Tasker_Server.TaskerService" behaviorConfiguration="TaskerServiceBehavior">
        <host>
          <baseAddresses>
             <add baseAddress="http://localhost:8000/TaskerTest/Service" />
          </baseAddresses>
        </host>
        <endpoint name="login" address="username" binding="wsHttpBinding"
              bindingConfiguration="Binding1"
              contract="Tasker_Server.ITasker" />
        <endpoint name="reg" address="reg" binding="wsHttpBinding"
              bindingConfiguration="Binding2"
              contract="Tasker_Server.Contracts.IRegister" />
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="Binding1" receiveTimeout="00:20:00">
          <security mode="Message">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
        <binding name="Binding2">
          <security mode="None">
            <transport clientCredentialType="None" />
            <message establishSecurityContext="false" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="TaskerServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom"
                                customUserNamePasswordValidatorType="Tasker_Server.CustomValidator, Tasker_Server" />
            <serviceCertificate findValue="localhost"
                            storeLocation="LocalMachine"
                            storeName="My"
                            x509FindType="FindBySubjectName" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

我就像这样启动Web服务:

ServiceHost selfHost = new ServiceHost(typeof(TaskerService));

try {
    selfHost.Open();
    Console.WriteLine("Service is up... (press <ENTER> to terminate)");
    Console.ReadLine();

    selfHost.Close();
}
catch (CommunicationException ce) {
    Console.WriteLine("Exception: {0}", ce.Message);
    Console.ReadLine();
    selfHost.Abort();
}

目前我只有两份合同:

[ServiceContract(Namespace="http://Tasker_Server")]
public interface ITasker {
    [OperationContract]
    string CheckCredentials(string username, string password);
}

[ServiceContract(Namespace="http://Tasker_Register")]
public interface IRegister {
    [OperationContract]
    string RegisterUser(string username, string password, string email);
}

我试图完成的是以下内容:

  1. 提供不安全的端点;客户可以调用此帐户并注册帐户。
  2. 提供安全端点(我使用UserName身份验证和自定义UserNamePasswordValidator),用户可通过该端点“登录”并调用所有操作。
  3. 我的软件中的这些东西现在都可以使用。我可以通过不安全的端点注册一个新帐户,我可以通过在客户端提供正确的ClientCredentials来调用安全端点。

    我的问题如下:

    1. 据我所知,通过使用UserName身份验证,每次客户端调用Web Service方法时,都会调用自定义验证程序中的Validation方法(这意味着每次都会运行数据库查询来检查凭据而不是您登录一次直到会话到期的网站)。以这种方式做事有什么根本性的错误吗?

    2. 我想到了另一种可能的方法来管理它(并以某种方式模拟网站的运作方式):

      • 仅对类似于“login”的操作使用安全端点(UserName身份验证)
      • 如果凭据正确,我创建一个新的GUID,将其保存在内存中并在用户名与该guid之间建立关联。
      • 然后所有操作都不需要UserName身份验证,但会有一个额外的参数(GUID):如果GUID在内存中并与用户关联,那么允许操作
      • 注销操作会破坏内存中的GUID。
      • 我认为我可以在此基础上使用SSL,以便不会以明确的方式发送GUID
      • 这是否违背了Web服务安全的目的,我只是想重新发明轮子?
    3. 哪种方法会更好?为什么?

      更新:添加了错误的配置文件。它来自客户端而不是Web服务。现在添加了正确的。

3 个答案:

答案 0 :(得分:2)

你使用guid的方法很好。请参阅其他问题WCF ticket base authentication并提及帖子

WCF Custom Message Headers

更新:方法比较。

第一种方法的好处是无状态/无会话的个别操作。客户端和服务都不需要记住先前身份验证调用的详细信息。但是,如果同一个客户端进行多次呼叫,最好记住故障单,而不是每次都记住并发送用户名和密码。 如果您担心重复调用数据库,可以缓存一段时间(例如30分钟)  用户名和密码(或更好的哈希值),并在进行数据库调用之前比较缓存字典中新请求的详细信息。

第二种方法需要在客户端保持状态(即票证),因此有点复杂。但它更安全,因为您不需要在整个会话期间记住用户名和密码。对于您的方案,我更喜欢票务方法。

顺便说一下,登录后我仍然建议使用https(通常性能损失不是必需的)但它会阻止网络嗅探器窃取故障单并使用它来代替用户进行一些恶意操作。

答案 1 :(得分:2)

2是一个合理的想法。它叫做Window Identity Framework。

与您建议的唯一真正区别在于,在联合安全模型中,您将身份验证与应用程序分开。在WIF中,您向发出令牌(您的GUID)的受信任机构进行身份验证。令牌内的加密是一组声明(允许的操作)。客户端将令牌作为其WCF调用的一部分传递,声明转移到安全原则,突然您的应用程序在没有任何WCF知识的情况下执行基于角色的安全性。

WIF的缺点是它很复杂,可能需要相当多的加速时间。

WIF的优势在于它由安全专家撰写。如果您实际构建的是处理金钱或敏感数据的商业应用程序,您应该仅仅因为这个原因使用WIF。

这是一个older article,但这是一个很好的动机。然后是MSDN

答案 2 :(得分:0)

你没有在WCF中实现你想要的using sessions吗?