401 Несанкционированный при отправке ajax-запроса в веб-API
Я почесал голову на этом в течение 2 дней. Я использую WebAPI версии 2.2 и использую CORS. Эта настройка работает на стороне сервера, мне разрешено получать авторизованный контент из кода моего веб-клиента, но я не авторизован в моих вызовах ajax.
Вот моя конфигурация:
Web API Config
WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
//enable cors
config.EnableCors();
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Filters.Add(new ValidationActionFilter());
}
}
Startup.Auth.cs:
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(UserContext<ApplicationUser>.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieHttpOnly = true,
CookieName = "Outpour.Api.Auth"
}
);
//app.UseCors(CorsOptions.AllowAll);
//app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
(Я пробовал каждую комбинацию app.UseCors(CorsOptions.AllowAll) и config.EnableCors())
Атрибут Мой контроллер:
[Authorize]
[EnableCors("http://localhost:8080", "*", "*", SupportsCredentials = true)]
[RoutePrefix("api/videos")]
public class VideosController : ApiController...
Веб-клиент
Ajax Call:
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
options.crossDomain = {
crossDomain: true
};
options.xhrFields = {
withCredentials: true
};
});
function ajaxGetVideoResolutionList() {
var request = {
type: "GET",
dataType: "json",
timeout: Outpour.ajaxTimeOut,
url: Outpour.apiRoot + "/videos/resolutions"
};
$.ajax(request).done(onAjaxSuccess).fail(onAjaxError);
Создание Cookie:
var result = await WebApiService.Instance.AuthenticateAsync<SignInResult>(model.Email, model.Password);
FormsAuthentication.SetAuthCookie(result.AccessToken, model.RememberMe);
var claims = new[]
{
new Claim(ClaimTypes.Name, result.UserName), //Name is the default name claim type, and UserName is the one known also in Web API.
new Claim(ClaimTypes.NameIdentifier, result.UserName) //If you want to use User.Identity.GetUserId in Web API, you need a NameIdentifier claim.
};
var authTicket = new AuthenticationTicket(new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie), new AuthenticationProperties
{
ExpiresUtc = result.Expires,
IsPersistent = model.RememberMe,
IssuedUtc = result.Issued,
RedirectUri = redirectUrl
});
byte[] userData = DataSerializers.Ticket.Serialize(authTicket);
byte[] protectedData = MachineKey.Protect(userData, new[] { "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware", DefaultAuthenticationTypes.ApplicationCookie, "v1" });
string protectedText = TextEncodings.Base64Url.Encode(protectedData);
Response.Cookies.Add(new HttpCookie("Outpour.Api.Auth")
{
HttpOnly = true,
Expires = result.Expires.UtcDateTime,
Value = protectedText
});
И последнее, но не менее важное, мои заголовки.
Remote Address:127.0.0.1:8888
Request URL:http://127.0.0.1/api/videos/resolutions
Request Method:GET
Status Code:401 Unauthorized
**Request Headersview source**
Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Host:127.0.0.1
Origin:http://localhost:8080
Pragma:no-cache
Proxy-Connection:keep-alive
Referer:http://localhost:8080/video/upload
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36
**Response Headersview source**
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost:8080
Cache-Control:no-cache
Content-Length:61
Content-Type:application/json; charset=utf-8
Date:Wed, 08 Oct 2014 04:01:19 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/8.0
WWW-Authenticate:Bearer
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
Инструменты разработчика и Fiddler утверждают, что куки не были отправлены с запросом.
2 ответа
Я полагаю, что вы смешиваете здесь проверку подлинности с помощью файлов cookie и токенов на предъявителя, вы не отправляете токен доступа в заголовке авторизации со своим запросом, поэтому вы продолжаете получать 401. Кроме того, вам нужно только разрешить использование CORS application.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
и удалите его еще где-нибудь из вашего атрибута контроллеров даже из конфигурации.
Посмотрите мой репо здесь, где я реализовал CORS, и интерфейс также AngularJS. это работает правильно. Вот и демоверсия в реальном времени для этого репо, откройте инструменты разработчика и следите за запросами, вы должны увидеть предполетный запрос до того, как увидите свой HTTP-запрос на получение.
Если вам нужно просто защитить свой API с помощью токенов на предъявителя, я рекомендую вам прочитать статью Аутентификация на основе токенов
Это может быть из-за того, что ваш API не находится на пришедшем URL-адресе в качестве вызывающего приложения. Ваш URL для API:http://127.0.0.1/
(игнорирование пути к папке - что не имеет значения)
..но ты звонишь из http://127.0.0.1:8888
который считается отдельным сайтом, так как порт отличается. Поскольку браузер считает, что сайт отличается, он не будет отправлять cookie.
Вы пытались протестировать его со страницы, размещенной по тому же URL-адресу, что и API (с тем же портом)?
Самое главное: убедитесь, что вы можете видеть, как Cookie отправляются через Fiddler.
Вы также можете найти более актуальную информацию о том, как заставить это работать над этим ответом.