向 web api 发送 ajax 请求时出现 401 Unauthorized
Posted
技术标签:
【中文标题】向 web api 发送 ajax 请求时出现 401 Unauthorized【英文标题】:401 Unauthorized when sending ajax request to web api 【发布时间】:2014-12-02 15:40:03 【问题描述】:我已经为此挠头 2 天了。我正在使用 WebAPI 2.2 版,并且正在使用 CORS。此设置适用于服务器端,我可以从我的 Web 客户端服务器代码中获取授权内容,但在我的 ajax 调用中获得未经授权的内容。
这是我的配置:
Web API 配置
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 调用:
$.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
开发者工具和提琴手声称没有随请求发送的 cookie。
【问题讨论】:
【参考方案1】:我相信您在这里混合了 cookie 身份验证和不记名令牌,您没有在请求的 Authorization 标头中发送访问令牌,这就是您不断收到 401 的原因。
同样,您只需要使用 application.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
允许 CORS 并将其从控制器属性中甚至从配置中删除。
检查我的Repo here,我在那里实现了CORS,前端也是AngularJS。它工作正常。这里也是这个 repo 的live demo,打开开发者工具并监控请求,你应该在看到你的 HTTP 获取请求之前看到 pre-flight 请求。
如果您只需要使用不记名令牌保护您的 API,那么我建议您阅读 Token Based Authentication 帖子
【讨论】:
我也有同样的问题,但是我不是使用WebApi作为资源主机,而是NancyFX。如果从 curl 左右调用,一切都可以找到,但不是从 AngularJS 应用程序调用。令牌端点很好,但如果涉及到 Nancy 提供的路由的 GET,ASP 网络身份验证会拒绝 401 的 OPTION 预检。我不知道如何克服这个问题。此外,我不确定 repo 是否与演示匹配: repo 在令牌请求中使用 client_id,演示显然不是。不相关,但表明两个版本不同。【参考方案2】:这可能是因为您的 API 不在作为调用应用程序的传入 URL 上。您的 API 网址是:
http://127.0.0.1/
(忽略文件夹路径-没关系)
..但是您从http://127.0.0.1:8888
调用它,因为端口不同,它被视为一个单独的站点。因为浏览器认为站点不同,所以不会发送cookie。
您是否尝试过从托管在与 API 相同 URL(具有相同端口)的页面上对其进行测试?
最重要的是:检查您是否可以看到在 Fiddler 中发送的 Cookie。
您还可以找到更多相关信息,了解如何使用此功能on this answer
【讨论】:
以上是关于向 web api 发送 ajax 请求时出现 401 Unauthorized的主要内容,如果未能解决你的问题,请参考以下文章
向本地服务器发送请求时出现 Flutter Web XMLHttpRequest 错误
尝试向 API 发送 POST 请求时出现属性错误 - Django
使用 Coldfusion/Railo 向 Mailchimp API v3.0 发送 PUT 请求时出现 401 未经授权的错误