バックグラウンド
認証 (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
- 作成された仮想ディレクトリ
- [プロジェクト] -> [プロパティ] -> [Web] を IIS Express に設定します
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>