2009-07-03 22 views
4

对于我的Web应用程序安全性,我使用FormsAuthentication/MembershipProvider和非持久性Cookie。在窗体身份验证Cookie中存储密码 - ASP.NET和WCF调用

我的应用程序与一些Web服务交互,这些也使用Membership提供程序。

用户密码在数据库中被散列。

问题在于,每次加载页面时,登录到应用程序的人都需要使用Web服务使用其用户名和密码对Web服务进行身份验证。但是一旦用户已经登录,他的密码在被哈希时不可检索。

我想知道密码是否可以存储在身份验证cookie中的securley,以便用户可以使用Web服务进行身份验证。

或者更好的主意!

编辑 我喜欢JOHNS IDEA以下,但有4条评论对我希望解决才去这条路线的机制......

回答

9

我同意@ John的回答,即使用一次性令牌比存储凭据要好。

对于令牌,您可以生成一些随机GUID并将其存储在数据库中。

作为不需要ASP.NET应用程序和WCF服务之间协调的替代方法,您可以将签名文档作为标记发送。

  1. 使用带符号时间,用户名和签名者名称(ASP.NET应用程序)创建XML或JSON文档。
  2. 生成上述文件的散列。
  3. 使用非对称加密(使用私钥)签名哈希。

WCF所要做的就是验证散列和签名。所以这不涉及到同一个数据库。使用签名时间,您可以在固定时间内过期令牌。

编辑:这个想法是基于public-key cryptography(也称为非对称密钥算法,公钥/私钥)。如果使用私钥加密某些内容,则只能使用相应的公钥将其解密;并且如果使用公钥对某些内容进行加密,则只能使用相应的私钥对其进行解密。有关代码在C#中的外观,请参阅Implementing RSA in C#。为什么这很有用?因为我们可以用它来实现数字签名。数字签名是证明我和我只写了一些东西,而不是其他人的一种方式。

继上述步骤生成的签名。你首先需要定义一个“让这个人在”文档中的规范形式。通常,非对称密钥算法无法处理太大的输入,因此您可以使用您的ASP.NET应用程序的专用密钥来加密哈希。生成的签名只能使用您的应用程序的公钥解密。最后,您可以将所有三个组件(原始文档,散列和签名)打包为XML或JSON等格式,并将其作为标记发送。

举个例子,假设你使用JSON格式的一切。首先,原来的“让这个家伙在文件”:

{"UserName":"Foo","SignedTime":"2009-07-09T00:00:00","Signer":"ASP.NET APP1"} 

接下来,生成上面的字符串,这是byte[]的SHA-1散列与modified Base64 encoding什么编码它,它看起来是这样的:

b2YgYW55IGNhcm5hbCBwbGVhc3VyZS4 

以上是虚假字符串,实际的东西可能看起来更长。然后,取散byte[]和使用RSA,生成另一个byte[]所以编码也有修改的Base64编码加密它:

mxlIGdlbmVyYXRpb24gb2Yga25vd2xfo34 

最后,你再拍JSON文件来存储所有以上。

{"UserName":"Foo","SignedTime":"2009-07-09T00:00:00","Signer":"ASP.NET APP1","Hash":"b2YgYW55IGNhcm5hbCBwbGVhc3VyZS4","Signature":"mxlIGdlbmVyYXRpb24gb2Yga25vd2xfo34"} 

最终的JSON文件成为你的密码的令牌。将它传递给WCF服务。 WCF服务取令牌,通过消除散列和签名构造原始文件:

{"UserName":"Foo","SignedTime":"2009-07-09T00:00:00","Signer":"ASP.NET APP1"} 

按照相同的算法来生成散列并验证它是相同的。使用ASP.NET应用程序的公钥解密签名,看看它是否成为哈希。此时,该文件被验证是由签名者签署的。检查当前时间和签名时间,看看令牌是否仍然有效。您所需要的只是在两个代码库之间分配公钥的方法,这些代码库可以从XML加载。

7

最好的做法是不要求用户用自己的用户名进行身份验证和每个请求的密码。

而是,在第一次身份验证时,Web服务应返回某种身份验证令牌。这是应该存储在什么地方。我建议将它存储在会话状态中,而不是表单身份验证票证中。

当来自Web服务的故障单过期时,您也可以考虑过期使用Forms身份验证故障单,这将导致用户需要再次登录到您的站点,提供您要验证的用户名和密码,以及然后用于再次验证Web服务,存储来自Web服务的票证等。

+1

请说明downvote的原因。如果没有人说错误是错误的,那么从错误中学习是不可能的。 – 2009-07-03 14:37:12

+0

你建议的方法听起来非常适合我的情况,但我对如何实现这一细节不清楚:1)如果WCF服务应该实现一个明确的登录方法验证用户名和密码,并返回认证令牌? 2)客户端凭证类型应该是什么? 3)什么是在后续请求中传递身份验证令牌的推荐机制(例如,作为每个服务方法的显式参数,或者像使用UserName认证时的用户名和密码那样的“幕后”)。 – AJM 2009-07-05 17:43:26

2

您设置的登录控制的Authenticate事件实例化一个新的服务代理,并在ClientCredentialsin设置的用户名/密码,代理的Web应用程序。

现在,当你拨打电话通过代理WCF服务将通过安全通道将这些凭据传递给服务,并利用它们进行身份验证。

现在你只需要存储的代理会话,并将其用于未来的访问服务,因为它具有信道状态和私钥。

protected void LoginControl_Authenticate(object sender, AuthenticateEventArgs e) 
{ 
    bool Authenticated = false; 
    try 
    { 
     MyServiceClient proxy = new MyServiceClient("MyServiceEndpoint"); 
     proxy.ClientCredentials.UserName.UserName = LoginControl.UserName; 
     proxy.ClientCredentials.UserName.Password = LoginControl.Password; 

     //It doesn't really matter what is called or what it does because 
     //Membership Provider for the Service does the authentication. 
     string retval = proxy.login("Logging in"); 

     //Now that channel is established the proxy needs to be kept 
     //since it contains the channel state which includes a private key 
     Session["MyServiceProxy"] = proxy; 
     Authenticated = true; 
    } 
    catch (Exception ex) 
    { 
     //Login Error... 
    } 
    e.Authenticated = Authenticated; 
}