我需要允许用户通过 SSO 或身份登录进行身份验证。
我可以让其中任何一个单独工作,但是当试图允许两者都工作时,我得到了这个:
SSO 按预期工作并且用户已通过身份验证。
身份登录成功后,用户将被重定向,并且在控制器中检查身份验证状态,但始终为 false。
我认为program.cs可能是错误的,这里是,以及控制器方法:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.UI.Services;
using Serilog.Sinks.MSSqlServer;
using Serilog;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
// Configure database connection
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
// Configure Identity services
builder.Services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false; // Adjust according to your needs
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
// Configure OpenID Connect Authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; // Use cookies by default
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; // Challenge with OpenID Connect
})
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.HttpOnly = false;
});
// Configure logging
Log.Logger = new LoggerConfiguration()
.WriteTo.MSSqlServer(
connectionString: builder.Configuration.GetConnectionString("DefaultConnection"),
sinkOptions: new MSSqlServerSinkOptions
{
TableName = "Logs",
AutoCreateSqlTable = true
},
restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information,
columnOptions: new ColumnOptions())
.CreateLogger();
builder.Host.UseSerilog();
// Add services and Razor Pages
builder.Services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
builder.Services.AddHttpContextAccessor();
// Configure CORS policy
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});
// Register other application services
builder.Services.AddTransient<IEmailTemplateService, EmailTemplateService>();
builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<SmtpSettings>(builder.Configuration.GetSection("SmtpSettings"));
builder.Services.AddTransient<ISiteService, SiteService>();
builder.Services.AddTransient<IDocumentService, DocumentService>();
builder.Services.AddTransient<IUtilitiesService, UtilitiesService>();
builder.Services.AddTransient<IRemediationsService, RemediationsService>();
builder.Services.AddTransient<IViewRenderService, ViewRenderService>();
builder.Services.AddTransient<IApplicationService, ApplicationService>();
builder.Services.AddTransient<IPdfService, PdfService>();
var app = builder.Build();
// Configure the HTTP request pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
// Set IronPdf license key
var provider = app.Services;
var configuration = provider.GetRequiredService<IConfiguration>();
IronPdf.License.LicenseKey = configuration.GetSection("Keys").GetValue<string>("IronPdf.LicenseKey");
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseFileServer();
app.UseRouting();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.UseStatusCodePagesWithRedirects("/Error/{0}");
app.Run();
用户登录方式:
[HttpPost]
[AllowAnonymous]
[Route("LoginUser")]
public async Task<IActionResult> LoginUser(LoginModel model, string returnUrl = null)
{
try
{
Log.Information("Login attempted: " + model.Email);
ViewData["ReturnUrl"] = returnUrl;
var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
Log.Information("Successful Identity login: " + User.Identity.Name);
return Ok(new { message = "Success", redirectUrl = Url.Action("Home", "Accounts") });
}
else
{
Log.Error("Invalid login: " + model.Email);
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return BadRequest(new { message = "Unsuccessful" });
}
}
catch (Exception ex)
{
Log.Error("Error on login: " + model.Email + " " + ex.Message);
ModelState.AddModelError(string.Empty, "Error login attempt.");
return BadRequest(new { message = ex.Message });
}
}
当我在
if (result.Succeeded)
上设置断点时就成功了
我们重定向到的控制器方法验证失败:
[Route("/accounts/home")]
public IActionResult Home()
{
if (User.Identity.IsAuthenticated)
{
return View();
}
return RedirectToAction("Login", "Home");
}
注意身份验证方案,Identity 的默认方案是 Identity.Application
您可以查看AddDefaultIdentity方法的代码:
public static IdentityBuilder AddDefaultIdentity<TUser>(this IServiceCollection services, Action<IdentityOptions> configureOptions) where TUser : class
{
services.AddAuthentication(o =>
{
o.DefaultScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies(o => { });
return services.AddIdentityCore<TUser>(o =>
{
o.Stores.MaxLengthForKeys = 128;
configureOptions?.Invoke(o);
})
.AddDefaultUI()
.AddDefaultTokenProviders();
}
public class IdentityConstants
{
private const string IdentityPrefix = "Identity";
/// <summary>
/// The scheme used to identify application authentication cookies.
/// </summary>
public static readonly string ApplicationScheme = IdentityPrefix + ".Application";
/// <summary>
/// The scheme used to identify bearer authentication tokens.
/// </summary>
public static readonly string BearerScheme = IdentityPrefix + ".Bearer";
/// <summary>
/// The scheme used to identify combination of <see cref="BearerScheme"/> and <see cref="ApplicationScheme"/>.
/// </summary>
internal const string BearerAndApplicationScheme = IdentityPrefix + ".BearerAndApplication";
/// <summary>
/// The scheme used to identify external authentication cookies.
/// </summary>
public static readonly string ExternalScheme = IdentityPrefix + ".External";
/// <summary>
/// The scheme used to identify Two Factor authentication cookies for saving the Remember Me state.
/// </summary>
public static readonly string TwoFactorRememberMeScheme = IdentityPrefix + ".TwoFactorRememberMe";
/// <summary>
/// The scheme used to identify Two Factor authentication cookies for round tripping user identities.
/// </summary>
public static readonly string TwoFactorUserIdScheme = IdentityPrefix + ".TwoFactorUserId";
}
在这里您将默认的身份验证方案修改为Cookies
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; // Use cookies by default
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; // Challenge with OpenID Connect
})
您必须使用
[Authorize(AuthenticationSchemes ="Identity.Application")]
或其他方式选择身份验证方案,您可以阅读此文档了解更多详细信息