我找到了关于社交登录的非常有用的科尔多瓦示例https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Samples/ 我已经在此示例之上构建了自己的应用程序,我非常坚持奇怪的问题。第一个输出来自示例,第二个来自我的应用程序。我不知道为什么在我的应用程序中使用“Identity.External”身份验证方案而不是“ServerCookie”。这导致身份验证失败。
示例的输出:
Microsoft.AspNetCore.Hosting.Internal.WebHost: Information: Request starting HTTP/1.1 GET http://localhost:54540/signin-facebook?code=***
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware: Information: AuthenticationScheme: ServerCookie signed in.
并从我的应用程序中输出:
Microsoft.AspNetCore.Hosting.Internal.WebHost: Information: Request starting HTTP/1.1 GET http://localhost:62892/signin-facebook?code=***
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware: Information: AuthenticationScheme: Identity.External signed in.
完整输出:
Microsoft.AspNetCore.Hosting.Internal.WebHost: Information: Request starting HTTP/1.1 GET http://localhost:62892/signin-facebook?code=***
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware: Information: AuthenticationScheme: Identity.External signed in.
Microsoft.AspNetCore.Hosting.Internal.WebHost: Information: Request finished in 1088.8914ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost: Information: Request starting HTTP/1.1 GET http://localhost:62892/connect/authorize?client_id=myClient&redirect_uri=http://localhost:62892/competitions/dashboard&response_type=token&provider=Facebook
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed for user: .
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker: Warning: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult: Information: Executing ChallengeResult with authentication schemes ().
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware: Information: AuthenticationScheme: ServerCookie was challenged.
Microsoft.AspNetCore.Hosting.Internal.WebHost: Information: Request finished in 45.0627ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost: Information: Request starting HTTP/1.1 GET http://localhost:62892/signin?ReturnUrl=%2Fconnect%2Fauthorize%3Fclient_id%3DmyClient%26redirect_uri%3Dhttp%3A%2F%2Flocalhost%3A62892%2Fcompetitions%2Fdashboard%26response_type%3Dtoken%26provider%3DFacebook
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker: Information: Executing action method FlyingDuck.Web.Controllers.Api.AuthenticationController.SignIn (FlyingDuck.Web) with arguments (/connect/authorize?client_id=myClient&redirect_uri=http://localhost:62892/competitions/dashboard&response_type=token&provider=Facebook) - ModelState is Valid
我的 Startup.cs 文件内容:
namespace MyProject.Web
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
builder.AddUserSecrets();
}
var wwwrootRoot = env.WebRootPath;
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.SignInScheme = "ServerCookie";
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyEntities")));
services.Configure<PasswordHasherOptions>(options =>
{
options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2;
});
services.AddScoped(ctx => new MyEntities(Configuration.GetConnectionString("MyEntities")));
services.AddScoped<ViewRender, ViewRender>();
services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(
options =>
{
options.User.RequireUniqueEmail = true;
options.Password.RequireUppercase = false;
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
}
)
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders();
services.AddSingleton(factory => new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
TypeNameHandling = TypeNameHandling.All
});
services.AddSession();
services.AddMvc();
services.AddSignalR(options =>
{
options.Hubs.EnableDetailedErrors = true;
options.Hubs.EnableJavaScriptProxies = false;
});
services.AddCors(o => o.AddPolicy("MyPolicy", b =>
{
b.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<IConnectionManager, ConnectionManager>();
// This code is responsible for disable redirect on unauthorized request.
// We don't need this feature. We want to get 401 instead of 302 redirection.
// http://stackoverflow.com/a/34332290
services.Configure<IdentityOptions>(options =>
{
options.Cookies.ApplicationCookie.AutomaticAuthenticate = true;
options.Cookies.ApplicationCookie.AutomaticChallenge = false;
options.Cookies.ApplicationCookie.AuthenticationScheme = "ApplicationCookie";
});
var builder = new ContainerBuilder();
builder.RegisterModule(new ServicesModule(Configuration));
builder.Populate(services);
var container = builder.Build();
return container.Resolve<IServiceProvider>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIdentity();
app.UseStatusCodePages();
app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch =>
{
branch.UseOAuthValidation(new OAuthValidationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
});
app.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api")), branch =>
{
branch.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = "ServerCookie",
CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie",
ExpireTimeSpan = TimeSpan.FromMinutes(60),
LoginPath = new PathString("/signin"),
LogoutPath = new PathString("/signout")
});
branch.UseFacebookAuthentication(new FacebookOptions()
{
AppId = "<removed>",
AppSecret = "<removed>"
});
});
app.UseOpenIdConnectServer(options =>
{
options.Provider = new AuthorizationProvider();
options.TokenEndpointPath = "/token";
options.AuthorizationEndpointPath = "/connect/authorize";
options.LogoutEndpointPath = "/connect/logout";
options.SigningCredentials.AddEphemeralKey();
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSession();
app.UseWebSockets();
app.UseSignalR();
app.UseMvc(routes =>
{
routes.MapRoute("SignalR", "signalr/{*all}");
routes.MapRoute("Home", "home/{*all}", new { controller = "Home", action = "Index" });
});
app.UseMvcWithDefaultRoute();
}
}
}
AuthorizationController 内容与示例中相同:
namespace MyProject.Web.Controllers.Api
{
[EnableCors(policyName: "MyPolicy")]
public class AuthorizationController : Controller
{
public AuthorizationController() {}
[Authorize, HttpGet("~/connect/authorize"), HttpPost("~/connect/authorize")]
public IActionResult Authorize()
{
var response = HttpContext.GetOpenIdConnectResponse();
if (response != null)
{
return View("Error", response);
}
var request = HttpContext.GetOpenIdConnectRequest();
if (request == null)
{
return View("Error", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred"
});
}
return Accept();
}
[Authorize, HttpPost("~/connect/authorize/accept")]
public IActionResult Accept()
{
var response = HttpContext.GetOpenIdConnectResponse();
if (response != null)
{
return View("Error", response);
}
var request = HttpContext.GetOpenIdConnectRequest();
if (request == null)
{
return View("Error", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred"
});
}
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
foreach (var claim in HttpContext.User.Claims)
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
identity.AddClaim(claim);
}
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetScopes(new[] {
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile,
/* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess
}.Intersect(request.GetScopes()));
ticket.SetResources("resource_server");
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
[Authorize]
[HttpPost("~/connect/authorize/deny")]
public IActionResult Deny()
{
var response = HttpContext.GetOpenIdConnectResponse();
if (response != null)
{
return View("Error", response);
}
var request = HttpContext.GetOpenIdConnectRequest();
if (request == null)
{
return View("Error", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred"
});
}
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
}
[HttpGet("~/connect/logout")]
public async Task<IActionResult> Logout(CancellationToken cancellationToken)
{
var response = HttpContext.GetOpenIdConnectResponse();
if (response != null)
{
return View("Error", response);
}
var identity = await HttpContext.Authentication.AuthenticateAsync(OpenIdConnectServerDefaults.AuthenticationScheme);
var request = HttpContext.GetOpenIdConnectRequest();
if (request == null)
{
return View("Error", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred"
});
}
return View("Logout", Tuple.Create(request, identity));
}
[HttpPost("~/connect/logout")]
public IActionResult Logout()
{
return SignOut("ServerCookie", OpenIdConnectServerDefaults.AuthenticationScheme);
}
}
}