当身份验证(JWT)成功时,我在本地开发环境中使用CORS。我的客户端页面运行localhost并调用api.mycompany.com获取数据。 我的api项目检查有效的JWT,如果通过,则返回内容。我花了一段时间才到这里,但这一切都很好。
如果我没有发送有效的JWT,api会正确响应401(在Fiddler中检查过),但客户端上的错误函数回调报告错误代码为0,状态为"错误&# 34。
我希望ajax回调函数检查错误的状态代码,如果是401,请检查标题名为location的标题(将包含uri到身份验证服务)。
(API项目)在本地IIS Express上运行MVC4项目的Visual Studio 2012实例
http://localhost:8080
http://api.mycompany.com:8080
在sites:
下的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>
(客户端项目)将Visual Studio实例与ASP.net空Web应用程序分开
http://localhost:22628
使用Google Chrome作为测试客户端
使用Fiddler查看流量
我认为这些应该是我的概念验证中的重要部分。再次,CORS预检和数据检索都可以正常工作。它只是未经授权的案例无效。如果您还有其他需要,请告诉我。谢谢你的帮助。
授权标头处理程序
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>
答案 0 :(得分:3)
我终于明白了。在Authorization Header Handler中,当tokenProcessor.IsValid为false时,我跳转到FailedResponseWithAddressToIdentityProvider,然后立即设置结果并将任务标记为完成。因此,我从未访问过Stuff Controller并添加了访问控制标题:
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;
}
答案 1 :(得分:0)
直接在ajax上检查状态代码可以使用此代码在onComplete上检查...
> $.ajaxSetup({
> error: function (x) {
> if (x.status == 401) {
> alert("401");
> }
> } });