在活动目录下具有基于信任的身份验证

时间:2011-09-29 21:15:33

标签: php javascript asp.net oauth cross-domain

我正在建立一个新项目,我正在讨论如何开发它。总体情况是开发一个可消费的JavaScript小部件,其他内部开发人员可以将其嵌入到他们的Web应用程序中。诀窍是消费者需要能够告诉我AD用户当前登录到他们的页面...然后我需要相信传递的用户名来自消费者并且没有被外部来源篡改

整体解决方案需要在消费方面进行非常简单的设置,不需要编译代码。它还需要在ASP.net和PHP应用程序中都能正常运行(因此我决定使用JavaScript)。

总的来说,它有点像Oauth解决方案......除了域之间的信任可以是固有的,因为我已经知道公司中的每个用户都信任主机域。

我开始将其删除并陷入困境。我的想法是,我基本上会托管客户端主机可以嵌入其页面的JavaScript文件。在他们的页面加载周期中,他们可以初始化我的JavaScript小部件并传递一个纯文本用户名(我真正需要的)。不知怎的,我会在客户端主机的网页和我的小部件之间建立一个安全的信任,这样第三方就不可能将我的小部件嵌入到虚假的网页中,并在他们自己以外的用户下发送动作命令。 / p>

我希望这对某人有意义。

1 个答案:

答案 0 :(得分:1)

我还没有真正找到答案,但我决定采用一种方法:

所以,我决定使用建议的jQuery UI Widget Factory编写JavaScript和HTML小部件的模式。这允许我的消费者使用简单的语法来实现小部件,如:

<script src="widget.js"></script>
$('#someElement').myWidget({ encryptionUrl: handlerPath }); 

现在,您会注意到,作为我的小部件的一部分,我要求消费者传递“handlerPath”。 “处理程序”只是一个Microsoft MVC控制器,它负责获取登录用户并加密呼叫。

所以我的应用程序中的处理程序看起来像这样......

[Authorize]
public JsonpResult GetToken(string body, string title, string sender)
{
    Packet token = new Packet();
    try
    {
       // Get the widget host's public cert
       string publicKey = "some.ssl.key.name.here";
       // Get the consumer host's private cert
       string privateKey = "this.consumers.ssl.key.name.here";

       // Build a simple message object containing secure details
       // Specifically, the Body will have action items (in JSON) from my widget
       // The User will be generated from the consumer's backend, thus secure 
       Message message = new Message(){
        Body = body,
        Title = title,
        User = System.Web.HttpContext.Current.User.Identity.Name,
        EncryptionServerIP = Request.UserHostAddress,
        Sender = new Uri(sender),
        EncryptionTime = DateTime.Now
       };

       PacketEncryption encryption = new PacketEncryption();
       // This class just wraps basic encryption and signing methods
       token = encryption.EncryptAndSign(message, publicKey, privateKey);
       token.Trust = "thisConsumerTrustName";
    }
       catch (Exception exception)
    {
       throw;
    }

   return this.Jsonp(token);
}

现在,我有一个加密的“令牌”,它已使用窗口小部件主机的公钥加密,并使用窗口小部件使用者的私钥进行签名。这个“令牌”通过消费服务器的JSONP传回小部件。

我的小部件然后将此“令牌”(仍为JSONP)发送到它的主机服务器。小部件托管服务器具有解密逻辑,看起来像这样。

public Message DecryptAndVerify(Packet packet, string requestIP)
{
    if (packet == null) throw new ArgumentNullException("packet");
    if (requestIP == null) throw new ArgumentNullException("requestIP");

    Message message = new Message();

    try
    {
        // Decrypt using the widget host's private key
        RSAEncryption decrypto = new RSAEncryption("MyPrivateKey");
        // Verify the signature using the "trust's" public key
        // This is important because like you'll notice, I get the trust name
        // from the encrypted packet. I then maintain a "trust store" mapping 
        // in my web.config, or SQL server
        RSAEncryption verifyo = new RSAEncryption(GetPublicKeyFromTrust(packet.Trust));

        string decryptedJson = decrypto.DecryptString(packet.EncryptedData);

        // Verify the signature
        if (!verifyo.Verify(decryptedJson, packet.Signature))
        {
        Exception ex = new Exception("Secure packet was not verified. Tamper evident");
        throw ex;
        }

        // If the message is encrypted correctly, turn it into a message object
        message = decryptedJson.FromJson<Message>();

        // Verify the ip
        if (message.EncryptionServerIP != requestIP)
        {
        Exception ex = new Exception("Request IP does not match encryption IP. Tamper evident");
        throw ex;
        }

        // Verify the time
        if ((DateTime.Now - message.EncryptionTime).Seconds > 30)
        {
        Exception ex = new Exception("Secure packet is too old");
        throw ex;
        }

    }
    catch (Exception ex)
        {
            throw ex;
        }
    return message;
}

这个想法是JavaScript小部件确定最终用户想要采取的安全操作。然后它回调它的主机(使用消费者提供的处理程序路径)并请求加密的令牌。该令牌包含调用者的IP地址,时间戳,当前AD用户名以及要完成的操作包。一旦小部件收到令牌,它就会将其传递给它自己的主机服务器,此时服务器会检查以确保它是

  • 根据预定义的信任进行正确签名和加密
  • 不超过30秒
  • 从与初始请求相同的IP到消费者服务器

在我确定这些检查有效后,我可以通过从字符串用户名创建WindowsPrincipal标识来执行用户的操作,如下所示:

WindowsPrincipal pFoo = new WindowsPrincipal(new WindowsIdentity("username"));
bool test = pFoo.IsInRole("some role");

所有的说法和完成,我已经从小部件使用者建立了一个受信任的请求,我不再需要提示进行身份验证。

希望这会帮助你。它已经在我的内部环境中运行了大约一个月的QA,到目前为止它的工作效果很好。