9

バックグラウンド

認証 (JWT) が成功すると、ローカルの開発環境で CORS が動作します。クライアント ページを localhost から実行し、データのために api.mycompany.com を呼び出しています。私の api プロジェクトは有効な JWT をチェックし、合格した場合はコンテンツを返します。ここにたどり着くまでに時間がかかりましたが、これはすべてうまくいきます。

有効な JWT を送信しない場合、API は 401 で適切に応答しますが (Fiddler でこれを確認)、クライアントのエラー関数コールバックはエラー コード 0 と「エラー」のステータスを報告します。

ajax コールバック関数でエラーのステータス コードをチェックし、それが 401 の場合は、location という名前のヘッダーのヘッダーをチェックします (認証サービスへの uri が含まれます)。

設定

  • (API プロジェクト) ローカル IIS Express で MVC4 プロジェクトを実行する Visual Studio 2012 インスタンス

    • ローカル ホスト ファイルは 127.0.0.1 を api.mycompany.com にマップします
    • [プロジェクト] -> [プロパティ] -> [Web] を IIS Express に設定します
      • ローカル IIS Express を使用 (チェック)
      • プロジェクトの URL:http://localhost:8080
      • 作成された仮想ディレクトリ
      • アプリケーションのルート URL を上書きする (オン)
      • アプリケーションのルート URL をオーバーライドします。http://api.mycompany.com:8080
    • サイトの下の applicationhost.config で:

      <site name="StuffManagerAPI" id="1">
        <application path="/" applicationPool="Clr4IntegratedAppPool">
          <virtualDirectory path="/" physicalPath="C:\Users\me\Documents\Visual Studio 2012\Projects\StuffManagerAPI\StuffManagerAPI" />
        </application>
        <bindings>
          <binding protocol="http" bindingInformation="*:8080:localhost" />
          <binding protocol="http" bindingInformation="*:8080:api.mycompany.com" />
        </bindings>
      </site>
      
  • (クライアント プロジェクト) ASP.net の空の Web アプリケーションを使用して Visual Studio インスタンスを分離する

    • [プロジェクト] -> [プロパティ] -> [Web] を IIS Express に設定します
      • ローカル IIS Express を使用 (チェック)
      • プロジェクトの URL:http://localhost:22628
      • 作成された仮想ディレクトリ
  • Google Chrome をテスト クライアントとして使用する

  • Fiddler を使用してトラフィックを表示する

コード

これらは、私の概念実証の重要な部分であるべきだと思います。ここでも、CORS プリフライトとデータ取得はすべて正常に機能します。機能していないのは、不正なケースだけです。他に何か必要な場合は、お知らせください。助けてくれてありがとう。

API プロジェクト

認証ヘッダー ハンドラー

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace StuffManagerAPI.Handlers
{
public class AuthorizationHeaderHandler : DelegatingHandler
{
    private const string KEY = "theKey";

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();

        const string identityProviderUri = "https://idp.mycompany.com";

        IEnumerable<string> apiKeyHeaderValues = null;
        if (request.Headers.TryGetValues("Authorization", out apiKeyHeaderValues))
        {
            var apiKeyHeaderValue = apiKeyHeaderValues.First();
            var token = apiKeyHeaderValue.Split(' ').LastOrDefault();
            var tokenProcessor = new JasonWebTokenDecryptor.JasonWebToken(token, KEY);

            if (tokenProcessor.IsValid)
            {
                base.SendAsync(request, cancellationToken).ContinueWith(t => taskCompletionSource.SetResult(t.Result));
            }
            else
            {
                var response = FailedResponseWithAddressToIdentityProvider(identityProviderUri);
                taskCompletionSource.SetResult(response);
            }

        }
        else
        {
            if(request.Method.Method != "OPTIONS")
            {
                //No Authorization Header therefore needs to redirect
                var response = FailedResponseWithAddressToIdentityProvider(identityProviderUri);
                taskCompletionSource.SetResult(response);
            }
            else
            {
                base.SendAsync(request, cancellationToken).ContinueWith(t => taskCompletionSource.SetResult(t.Result));
            }
        }

        return taskCompletionSource.Task;
    }

    private static HttpResponseMessage FailedResponseWithAddressToIdentityProvider(string identityProviderUri)
    {
        // Create the response.
        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        response.Headers.Add("Location", identityProviderUri);
        return response;
    }
}
}

スタッフコントローラー

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;
using StuffManagerAPI.Attributes;
using StuffManagerAPI.Models;

namespace StuffManagerAPI.Controllers
{
[HttpHeader("Access-Control-Allow-Origin", "*")]
[HttpHeader("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE")]
[HttpHeader("Access-Control-Allow-Headers", "Authorization")]
[HttpHeader("Access-Control-Expose-Headers", "Location")]
public class StuffController : ApiController
{
    private readonly Stuff[] _stuff = new[]
        {
            new Stuff
                {
                    Id = "123456",
                    SerialNumber = "112233",
                    Description = "Cool Item"
                },
            new Stuff
                {
                    Id = "456789",
                    SerialNumber = "445566",
                    Description = "Another Cool Item"
                }
        };

    public Stuff Get(string id)
    {
        var item = _stuff.FirstOrDefault(p => p.Id == id);
        if (item == null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }

        return item;
    }

    public IEnumerable<Stuff> GetAll()
    {
        return _stuff;
    }

    public void Options()
    {
       // nothing....
    }

}
}

クライアントプロジェクト

main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>ASP.NET Web API</title>
    <link href="../Content/Site.css" rel="stylesheet" />
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.js"></script>

    <script type="text/javascript">
        var baseUrl = "http://api.mycompany.com:8080/api/";
        $.support.cors = true;

        $(document).ready(
            getListofStuff()
        );

        function setHeader(xhr) {
            xhr.setRequestHeader('authorization', 'Bearer blah.blah.blah');
        }

        function getListofStuff() {
            var apiUrl = baseUrl + "stuff/";

            $.ajax({
                url: apiUrl,
                type: 'GET',
                dataType: 'json',
                crossDomain: true,
                success: receivedListOfStuff,
                error: receiveError,
                beforeSend: setHeader,
                statusCode: {
                    0: function() {
                        alert('got 0');
                    },
                    401: function () {
                        alert('finally got a 401');
                    }
                }
            });
        }

        function getIndividualPieceOfStuff(id) {
            var apiUrl = baseUrl + "stuff/" + id;

            $.ajax({
                url: apiUrl,
                type: 'GET',
                dataType: 'json',
                crossDomain: true,
                success: receivedIndividualStuffItem,
                error: receiveError,
                beforeSend: setHeader
            });
        }

        function receivedListOfStuff(data) {
            $.each(data, function (key, val) {

                var listItem = $('<li/>').text(val.Description);
                listItem.data("content", { id: val.Id});
                $(".myStuff").prepend(listItem);
            });

            $(".myStuff li").click(function () {
                getIndividualPieceOfStuff($(this).data("content").id);
            });
        }

        function receivedIndividualStuffItem(data) {
            $("#stuffDetails #id").val(data.Id);
            $("#stuffDetails #serialNumber").val(data.SerialNumber);
            $("#stuffDetails #description").val(data.Description);
        }

        function receiveError(xhr, textStatus, errorThrown) {
            var x = xhr.getResponseHeader("Location");
            var z = xhr.responseText;

            if (xhr.status == 401){
                alert('finally got a 401');
               }

            alert('Error AJAX');
        }
    </script>

</head>
<body>
.
.
.
.
</body>
</html>
4

2 に答える 2

3

私はついにそれを理解しました。Authorization ヘッダー ハンドラーで、tokenProcessor.IsValid が false の場合、FailedResponseWithAddressToIdentityProvider にジャンプし、すぐに結果を設定してタスクを完了としてマークします。したがって、Stuff Controller にアクセスして Access Control ヘッダーを追加することはありません。

if (tokenProcessor.IsValid)
{
    base.SendAsync(request, cancellationToken).ContinueWith(t => taskCompletionSource.SetResult(t.Result));
}
else
{
    var response = FailedResponseWithAddressToIdentityProvider(identityProviderUri);
            taskCompletionSource.SetResult(response);
}
.
.
.
private static HttpResponseMessage FailedResponseWithAddressToIdentityProvider(string identityProviderUri)
{
    // Create the response.
    var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
    response.Headers.Add("Location", identityProviderUri);
    return response;
}

}

おそらくこれを行うためのより良い方法がありますが、FailedResponseWithAddressToIdentityProvider メソッドの応答にヘッダーを追加しただけで、ブラウザーは最終的に Chrome、Firefox、および IE8 で 401 を認識します。変更点は次のとおりです。

private static HttpResponseMessage FailedResponseWithAddressToIdentityProvider(string identityProviderUri)
{
    // Create the response.
    var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
    response.Headers.Add("Location", identityProviderUri);
    response.Headers.Add("Access-Control-Allow-Origin", "*");
    response.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE");
    response.Headers.Add("Access-Control-Allow-Headers", "Authorization");
    response.Headers.Add("Access-Control-Expose-Headers", "Location");
    return response;
}
于 2013-01-25T15:57:43.967 に答える
0

ajaxでステータスコードを直接チェックするのではなく、このコードを使用してonCompleteでチェックできます...

> $.ajaxSetup({
>     error: function (x) {     
>         if (x.status == 401) {
>             alert("401");
>         }
>     } });
于 2013-01-22T05:12:32.237 に答える