我正在使用ASP.net Core 1,MVC 6.我使用SignInManager和UserManager来验证来自另一个MVC应用程序的web api应用程序(MVC6 / C#)中的用户(web api Logon方法是实际上是从Jquery Ajax请求中调用的)。
在IE中,我调用了 Login 方法,成功后,它为我提供了一个带有ASP.net身份验证cookie的Set-Cookie响应。然后我可以看到后续请求附加了ASP.net auth cookie。
在chrome中,响应中返回了Set-Cookie指令,但后续请求没有附加cookie。
为什么会这样?
我能看到的唯一区别是,在Chrome中,有一个正在发送的 OPTIONS 请求,但我已经在web api的startup.cs文件中处理了该请求无视它。
我登录web api的请求如下:
Accept */*
Accept-Encoding gzip, deflate
Accept-Language en-IE
Cache-Control no-cache
Connection Keep-Alive
Content-Length 246
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Cookie BeaeN4tYO5M=CfDJ8KMNkK4F2ylMlo1LFxNzxWLNDECVWfhxBYRQrw_MkNQBrVIwfO6FoMIMqg1PP-nZa8Dhp3IV1ZS1uXKpknUDYegiMlEvFaNG-wqUXErvQ5wkMMc_HBI88j-7bCbD2Q7P_B6fEQOQSTKHoL5sTcH0MoM
DNT 1
Host localhost:44338
Referer https://localhost:44356/
Request POST /api/account/Login HTTP/1.1
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
X-ACL-Key 4A6F0007-95E7-4423-B786-6FBF981799FE
响应如下:
Response HTTP/1.1 200 OK
Cache-Control no-cache
Pragma no-cache
Content-Type application/json; charset=utf-8
Expires -1
Server Kestrel
Set-Cookie oAuthInterop=CfDJ8Asqo6qO2cNHlXpsdNLsuoQWhLxXcnaNkAMTB-VvpkMRIz2AiM_7feoIM29gza_zZz97qaE6TKdqK8y1jDPjDDyiiMdOMiuCmCoV5X4IQ9xtHvpGgmFoxOSiYFVeVOBbHsLx4BccL647F9sJ07M55zvjMx_7wrt32omhONH64vmc12P3nepwZjNSIFYfom1U0Z4r4EX_0tZjKRH7FrdvO0PI2iY5SMaKhCcBw1QXpQHSUxL6Hm-Wr8Q46gFAYoa6YffJV0Rx80FvJHmr1LMAA6PAF0dU_DzNdRVHdXm14t_nbfl-6xb6o7WQN259moUhkT1ZQ9CZsYwWvn7VBmpjfIXNJvIu0FDnRaHnNMrj3uN77_cAMdO3OcyCuy-CAKJ9c-0PxKToStb9juGSNa9ClpVQPADzpUxFqxZU029AXBPavXQK2Ezvy7YT4FwCkL8TEf5AnB5hfOZ5YCBlqD30n2heMdHDbXRHpxeaQB4aoY_6uSpJ3cPazBDsbvGi4fV2-0g5NvoTGgJUXa5p4UntRmuiJ2tZHbMmEjXzf-GV6QtTFIhseKsS3n6TMX68yqQOhYOzxvHdJXPjYxvjmm6-vJw5w2FDgiEXoQJQ7qaSmGzRwOA_cE4VBV_RhzrZELmp3A; path=/; secure; httponly
X-SourceFiles =?UTF-8?B?QzpcVXNlcnNcUm9iZXJ0XERlc2t0b3BcSEJFIE1hbmFnZXJcTUFJTlxCbHVlem9uZSBXZWJBcGlcc3JjXEJ6LkFwcGxpY2F0aW9uXEJ6LkFwcGxpY2F0aW9uLkFwaVx3d3dyb290XGFwaVxhY2NvdW50XExvZ2lu?=
X-Powered-By ASP.NET
Access-Control-Allow-Methods GET,PUT,POST,DELETE
Access-Control-Allow-Headers Content-Type,x-xsrf-token,X-ACL-Key
Date Fri, 06 May 2016 14:23:22 GMT
Content-Length 16
后续测试web api调用(IsLoggedIn):
Request GET /api/account/IsLoggedIn HTTP/1.1
X-ACL-Key 4A6F0007-95E7-4423-B786-6FBF981799FE
Accept */*
Referer https://localhost:44356/
Accept-Language en-IE
Accept-Encoding gzip, deflate
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Host localhost:44338
DNT 1
Connection Keep-Alive
Cache-Control no-cache
Cookie BeaeN4tYO5M=CfDJ8KMNkK4F2ylMlo1LFxNzxWLNDECVWfhxBYRQrw_MkNQBrVIwfO6FoMIMqg1PP-nZa8Dhp3IV1ZS1uXKpknUDYegiMlEvFaNG-wqUXErvQ5wkMMc_HBI88j-7bCbD2Q7P_B6fEQOQSTKHoL5sTcH0MoM; oAuthInterop=CfDJ8Asqo6qO2cNHlXpsdNLsuoQWhLxXcnaNkAMTB-VvpkMRIz2AiM_7feoIM29gza_zZz97qaE6TKdqK8y1jDPjDDyiiMdOMiuCmCoV5X4IQ9xtHvpGgmFoxOSiYFVeVOBbHsLx4BccL647F9sJ07M55zvjMx_7wrt32omhONH64vmc12P3nepwZjNSIFYfom1U0Z4r4EX_0tZjKRH7FrdvO0PI2iY5SMaKhCcBw1QXpQHSUxL6Hm-Wr8Q46gFAYoa6YffJV0Rx80FvJHmr1LMAA6PAF0dU_DzNdRVHdXm14t_nbfl-6xb6o7WQN259moUhkT1ZQ9CZsYwWvn7VBmpjfIXNJvIu0FDnRaHnNMrj3uN77_cAMdO3OcyCuy-CAKJ9c-0PxKToStb9juGSNa9ClpVQPADzpUxFqxZU029AXBPavXQK2Ezvy7YT4FwCkL8TEf5AnB5hfOZ5YCBlqD30n2heMdHDbXRHpxeaQB4aoY_6uSpJ3cPazBDsbvGi4fV2-0g5NvoTGgJUXa5p4UntRmuiJ2tZHbMmEjXzf-GV6QtTFIhseKsS3n6TMX68yqQOhYOzxvHdJXPjYxvjmm6-vJw5w2FDgiEXoQJQ7qaSmGzRwOA_cE4VBV_RhzrZELmp3A
响应如下:
Response HTTP/1.1 200 OK
Content-Type application/json; charset=utf-8
Server Kestrel
X-SourceFiles =?UTF-8?B?QzpcVXNlcnNcUm9iZXJ0XERlc2t0b3BcSEJFIE1hbmFnZXJcTUFJTlxCbHVlem9uZSBXZWJBcGlcc3JjXEJ6LkFwcGxpY2F0aW9uXEJ6LkFwcGxpY2F0aW9uLkFwaVx3d3dyb290XGFwaVxhY2NvdW50XElzTG9nZ2VkSW4=?=
X-Powered-By ASP.NET
Access-Control-Allow-Methods GET,PUT,POST,DELETE
Access-Control-Allow-Headers Content-Type,x-xsrf-token,X-ACL-Key
Date Fri, 06 May 2016 14:23:22 GMT
Content-Length 68
我登录web api的请求如下:
POST /api/account/Login HTTP/1.1
Host: localhost:44338
Connection: keep-alive
Content-Length: 246
Accept: */*
Origin: https://localhost:44356
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36
X-ACL-Key: 4A6F0007-95E7-4423-B786-6FBF981799FE
Referer: https://localhost:44356/
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
响应如下:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Vary: Origin
Server: Kestrel
Set-Cookie: oAuthInterop=CfDJ8Asqo6qO2cNHlXpsdNLsuoRvlRjfUBWrkt3W3NzBJIoFYA6DcQivnfYmZV2O5xuiqpd75oRjZ-JeHBcjiOK0HoFJQ9f61RyJ2HDeuCNmQk0H-pA3Lzs5ft_F49dpQt0kFn3_-FzEh5-NScCbY4N6TiuYlWY4VSoKsdJJ91k7Z4LQO-0Wm3cZ6HfX0E6pLzGG4lWaZGuV-gOsVCRygR5nv_O_YpWwfaLsT_51aX6fNXVSotU6MECEkFdfWseqOGyYVj7KJrxY2mPwksE0XGACs12TnmfJzCABrzd06FnTPy3RuqJF2IWOobX6ZAHGMoAVFR07mhy9gMPyaHQ12RKmhBhZSXE-Yi3BHow2ER9d2Niligx7JjwYR7UfHFHWJdoYzewLRkZZGE5pw67O710hYyA2UCM2ODB9l9x-WDQ1A_3xjxu2Mrkp0lrF0V-h3y6V2gzEP9RyQAjDISEEZQqvb-GzfZrsRzzQcMn0TMhq5_LUKkX3AScSGRiarBzZ2O9Af3jzwTmN1BciJknJwMKRefq_zrXH7kymCD1kJM89aGkswqp2bycMQjlsjqg5k8EEhv8u1kLA7hA9NyE2ZaamB1PAWYz4NXi3Agccgw83nFi4bs6VE8ZLnyZFEwxdyEGyvQ; path=/; secure; httponly
Access-Control-Allow-Origin: https://localhost:44356
Access-Control-Allow-Credentials: true
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcUm9iZXJ0XERlc2t0b3BcSEJFIE1hbmFnZXJcTUFJTlxCbHVlem9uZSBXZWJBcGlcc3JjXEJ6LkFwcGxpY2F0aW9uXEJ6LkFwcGxpY2F0aW9uLkFwaVx3d3dyb290XGFwaVxhY2NvdW50XExvZ2lu?=
X-Powered-By: ASP.NET
Access-Control-Allow-Methods: GET,PUT,POST,DELETE
Access-Control-Allow-Headers: Content-Type,x-xsrf-token,X-ACL-Key
Date: Fri, 06 May 2016 12:59:36 GMT
Content-Length: 16
后续测试web api调用(IsLoggedIn):
GET /api/account/IsLoggedIn HTTP/1.1
Host: localhost:44338
Connection: keep-alive
Accept: */*
Origin: https://localhost:44356
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36
X-ACL-Key: 4A6F0007-95E7-4423-B786-6FBF981799FE
Referer: https://localhost:44356/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
响应如下:
HTTP/1.1 401 Unauthorized
Content-Length: 0
Content-Type: text/plain; charset=utf-8
Server: Kestrel
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcUm9iZXJ0XERlc2t0b3BcSEJFIE1hbmFnZXJcTUFJTlxCbHVlem9uZSBXZWJBcGlcc3JjXEJ6LkFwcGxpY2F0aW9uXEJ6LkFwcGxpY2F0aW9uLkFwaVx3d3dyb290XGFwaVxhY2NvdW50XElzTG9nZ2VkSW4=?=
X-Powered-By: ASP.NET
Access-Control-Allow-Methods: GET,PUT,POST,DELETE
Access-Control-Allow-Headers: Content-Type,x-xsrf-token,X-ACL-Key
Date: Fri, 06 May 2016 12:59:43 GMT
我的web api控制器代码如下所示:
[Authorize]
[EnableCors("AllowAll")]
[Route("api/[controller]")]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[HttpPost("login")]
[AllowAnonymous]
public async Task<IActionResult> Login(UserLogin model)
{
if (ModelState.IsValid) {
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) {
return Json(new { success = true });
}
if (result.RequiresTwoFactor) {
return Json(new { success = false, errType = 1 });
}
if (result.IsLockedOut) {
return Json(new { success = false, errType = 2 });
} else {
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Json(new { success = false, errType = 3 });
}
}
return Json(new { success = false, errType = 0 });
}
[HttpGet("IsLoggedIn")]
public IActionResult IsLoggedIn()
{
return Json(new {
loggedon = (HttpContext.User.Identity.Name != null && HttpContext.User.Identity.IsAuthenticated),
isauthenticated = HttpContext.User.Identity.IsAuthenticated,
username = HttpContext.User.Identity.Name
});
}
}
我的web api的Startup.cs如下所示:
public class Startup
{
public static int SessionLength { get; private set; }
private string Connection;
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
SessionLength = 30;
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Get the configured connection string.
Connection = Configuration["Data:DefaultConnection:ConnectionString"];
var userStore = new CustomUserStore();
var roleStore = new CustomRoleStore();
var userPrincipalFactory = new CustomUserPrincipalFactory();
services.AddInstance<IUserStore<ApplicationUser>>(userStore);
services.AddInstance<IRoleStore<ApplicationRole>>(roleStore);
services.AddInstance<IUserClaimsPrincipalFactory<ApplicationUser>>(userPrincipalFactory);
services.AddIdentity<ApplicationUser, ApplicationRole>(options => {
options.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents() {
OnRedirectToAccessDenied = ctx =>
{
if (ctx.Response.StatusCode == (int)HttpStatusCode.Unauthorized || ctx.Response.StatusCode == (int)HttpStatusCode.Forbidden) {
return Task.FromResult<object>(null);
}
ctx.Response.Redirect(ctx.RedirectUri);
return Task.FromResult<object>(null);
},
OnRedirectToLogin = ctx =>
{
if (ctx.Response.StatusCode == (int)HttpStatusCode.Unauthorized || ctx.Response.StatusCode == (int)HttpStatusCode.Forbidden) {
return Task.FromResult<object>(null);
}
ctx.Response.Redirect(ctx.RedirectUri);
return Task.FromResult<object>(null);
}
};
//options.Cookies.ApplicationCookie.CookieHttpOnly = false;
options.Cookies.ApplicationCookieAuthenticationScheme = "ApplicationCookie";
options.Cookies.ApplicationCookie.AuthenticationScheme = "ApplicationCookie";
options.Cookies.ApplicationCookie.CookieName = "oAuthInterop";
options.Cookies.ApplicationCookie.AutomaticChallenge = true;
options.Cookies.ApplicationCookie.AutomaticAuthenticate = true;
options.Cookies.ApplicationCookie.DataProtectionProvider = new DataProtectionProvider(new DirectoryInfo("d:\\development\\artefacts"),
configure =>
{
configure.SetApplicationName("TestAuthApp");
//configure.ProtectKeysWithCertificate("thumbprint");
});
options.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromMinutes(SessionLength);
}).AddDefaultTokenProviders();
// Add framework services.
services.AddMvc();
// Add cross site calls.
//TODO: implement with better security instead of allowing everything through.
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader().AllowCredentials()));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc();
}
}
答案 0 :(得分:5)
一个疯狂的猜测是,当您通过ajax从javascript发出跨域请求时,您没有在XMLHttpRequest
上设置withCredentials标记。此标志基本上控制是否在跨域请求中包含凭据(例如 cookie ,授权标头或客户端证书)。为什么它仍然适用于IE?不完全确定,但也许是因为这个标志的正确实现只出现在IE10中,你可能会使用另一个版本的IE。如果您使用jquery发出请求,请参阅here如何设置此标志。
如果不是这种情况,请在Chrome的OPTIONS请求中包含您的客户端代码+请求和响应标头。