1

I am trying to make an ajax call (using IE 10) to a page that returns json (not jsonp) but I keep getting a "401 - Unauthorized: Access is denied due to invalid credentials." The site is setup in IIS to use "Windows Authentication", however, if I change the site to enable Anonymous Authentication the call works. Below is the code I am using to make the call. What am I missing with my call or what do I need to change on my webserver? The Windows Authentication is currently set up to use NTLM authentication on the Windows Auth.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="scripts/jquery-2.0.3.min.js"></script>
    <script src="scripts/base64.js"></script>
    <script type="text/javascript">
        function QueryMyData() {
            var postUrl = 'http://mydevpage/storage.ashx';
            var data = 'AssetNumber=102405';
            $.support.cors = true;
            $.ajax({
                type: "POST",
                url: postUrl,
                data: data,
                dataType: 'json',
                crossDomain: true,
                cache: false,
                username: "mydomain.net\\myuser",
                password: "password",
                beforeSend: function (xhr) {
                    xhr.withCredentials = true;

                },
                success: function (result) {
                    if (result) {
                        if (result.error)
                            alert(result.error);
                        else
                            alert(result.id);
                    }
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    alert('Unknow Error:' + thrownError + ajaxOptions + xhr.status + " " + xhr.statusText);
                }
            });
        }
        QueryMyData();
    </script>
</head>
<body>
</body>
</html>
4

1 回答 1

1

I found a solution to my problem. While I was not ever able to get the ajax request to work with security hitting a page on another domain, I did find a way to accomplish this. I ended up creating a ProxyHandler.ashx page and setting the permission on the request using the WebClient.

html page

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
    <script type="text/javascript">
        function QueryMyData() {
            var postUrl = './ProxyHandler.ashx?http://mydevpage/storage.ashx';
            var data = 'AssetNumber=102405';
            $.support.cors = true;
            $.ajax({
                type: "POST",
                url: postUrl,
                data: data,
                dataType: 'json',
                cache: false,
                success: function (result) {
                    if (result) {
                        if (result.error)
                            alert(result.error);
                        else
                            alert(result.id);
                    }
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    alert('Unknow Error:' + thrownError + ajaxOptions + xhr.status + " " + xhr.statusText);
                }
            });
        }
        QueryMyData();
    </script>
</head>
<body>
</body>
</html>

Here is the proxy page (ProxyHandler.ashx)

public class ProxyHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string username = "svcMyServiceAccount";
        string password = "password";
        try
        {
            string uri = context.Request.RawUrl.Substring(context.Request.RawUrl.IndexOf("?") + 1);

            if (uri.StartsWith("ping"))
            {
                context.Response.Write("<html><body>Hello ProxyHandler</body></html>");
                return;
            }

            context.Response.ContentType = "text/plain";

            byte[] bytes = new byte[context.Request.InputStream.Length];
            context.Request.InputStream.Read(bytes, 0, (int)context.Request.InputStream.Length);
            var data = System.Text.Encoding.UTF8.GetString(bytes);

            using (System.Net.WebClient wc = new System.Net.WebClient())
            {
                wc.Headers["Content-Type"] = "application/x-www-form-urlencoded";
                //this is the magic of getting auth passed.  See post http://stackoverflow.com/questions/1680718/domain-credentials-for-a-webclient-class-dont-work
                wc.Credentials = CreateCredientialCached(uri, username, password, "mydomain");
                var response = wc.UploadString(new Uri(uri, UriKind.Absolute), "POST", data);
                context.Response.Write(response); //already in the JSON Reponse class format
            }
        }
        catch (Exception e)
        {
            context.Response.Write(GetJSON(string.Empty, e));
        }
    }

    private CredentialCache CreateCredientialCached(string uri, string userName, string userPassword, string domain)
    {
        CredentialCache cc = new CredentialCache();
        cc.Add(new Uri(uri), "NTLM", new NetworkCredential(userName, userPassword, domain));
        return cc;
    }

    private string GetJSON(string id, Exception error)
    {
        var json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(new Response() { id = id, error = error != null ? error.ToString() : string.Empty });
        return json;
    }

    // Necessary for IHttpHandler implementation
    public bool IsReusable
    {
        get { return false; }
    }

    private class Response
    {
        public string id { get; set; }
        public string error { get; set; }
    };
}
于 2013-11-04T16:56:48.383 回答