我有一个正在使用基本身份验证的ASP.NET Core 2.2 Web API。到目前为止,它运行良好,没有任何问题。在控制器之一中,用一种[AllowAnonymous]
装饰一种操作方法,以照常进行用户登录。
[Produces("application/json")]
[Route("user")]
[AllowAnonymous]
[ApiController]
public class LoginController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IMessagingService _messageService;
private readonly IBasicAuthenticationService _basicAuthenticationService;
private readonly string PWAPIBaseUrl;
public LoginController(IConfiguration configuration, ILogger<LoginController> logger, IMessagingService messagingService, IBasicAuthenticationService authenticationService)
{
_configuration = configuration;
_logger = logger;
_messageService = messagingService;
_basicAuthenticationService = authenticationService;
}
[HttpGet]
[AllowAnonymous]
[Route("login/{username}/{clientID}")]
public async Task<IActionResult> UserLogin(string username, string clientID)
{
// Check the Credentials Manually
string failReason = "";
if (!CheckCredentials(out failReason))
{
return StatusCode(StatusCodes.Status403Forbidden, userInfo);
}
// Load the Roles and UI Preferences ...
}
}
.NET Core 2.2即将结束时,我已尝试升级到.NET Core 3.1,并遵循official Migration Guide,并有必要进行changes。尽管应用程序启动顺利,但是仍然存在一个错误问题,无法进行升级。
在上述控制器上,不会忽略[AllowAnonymous],并且会评估身份验证并抛出错误。但是登录方法是在之后执行的。这将导致Login在所有从属应用程序中中断。我尝试了来自Stackoverflow的所有建议,例如this,this和this。
基本身份验证处理程序:
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly ILogger<BasicAuthenticationHandler> _logger = null;
private readonly IBasicAuthenticationService _basicAuthenticationService;
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
UrlEncoder encoder,
ILoggerFactory loggerFactory,
ISystemClock clock,
IBasicAuthenticationService authenticationService)
: base(options, loggerFactory, encoder, clock)
{
_logger = loggerFactory.CreateLogger<BasicAuthenticationHandler>();
_basicAuthenticationService = authenticationService;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var config = Util.GetConfig();
if (!Request.Headers.ContainsKey("Authorization"))
{
_logger.LogError("No authorization credentials");
return AuthenticateResult.NoResult();
}
if (!Request.Headers.ContainsKey("ClientID"))
{
_logger.LogError("Missing header client token");
return AuthenticateResult.Fail("Missing header client token");
}
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
if (authHeader.Scheme != "Basic")
{
_logger.LogError("Authentication scheme not recognized");
return AuthenticateResult.Fail("Authentication scheme not recognized");
}
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
var username = credentials[0];
var password = credentials[1];
string fullname = "";
string failReason = "";
bool t = false;
IPrincipal principal = null;
// Do Business Validation against the DB
if (!t) // login failed
{
byte[] bEncodedResponse = Encoding.UTF8.GetBytes(failReason);
await Context.Response.Body.WriteAsync(bEncodedResponse, 0, bEncodedResponse.Length);
return AuthenticateResult.Fail(failReason);
}
else
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, username),
new Claim(ClaimTypes.Name, fullname),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
principal = principal==null?new ClaimsPrincipal(identity): principal;
var ticket = new AuthenticationTicket(principal as ClaimsPrincipal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
}
Startup.cs
public class Startup
{
public Startup(IWebHostEnvironment environment, IConfiguration configuration, ILoggerFactory loggerFactory)
{
Environment = environment;
Configuration = configuration;
LoggerFactory = loggerFactory;
}
public IConfiguration Configuration { get; }
public ILoggerFactory LoggerFactory { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
// Adding the Configuration Options -- Extension Methods to Inject Configuration as IOption POCOs
services.ConfigureAPIOptions(Configuration);
// configure DI for application services -- Other DI Objects
services.ConfigureDependencies(Configuration, LoggerFactory);
Common.APIConfiguration.Current = Configuration;
services.AddControllers();
services.AddAuthorization();
if (Environment.IsDevelopment())
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "My Materials API", Version = "v1" });
});
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Materials API v1");
c.RoutePrefix = string.Empty;
});
}
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
我仍然对做错什么一无所知,并且可能会丢失ASP.NET Core 3.1中的某些内容。请帮助我使它工作。预先感谢。
编辑1:
ServiceExtensions.cs
public static class ServiceExtensions
{
public static void ConfigureAPIOptions(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<DataSetting>(configuration.GetSection("DataSettings"));
services.Configure<UrlSetting>(configuration.GetSection("UrlSettings"));
services.Configure<SiteSettings>(configuration.GetSection("SiteSettings"));
}
public static void ConfigureDependencies(this IServiceCollection services, IConfiguration configuration, ILoggerFactory loggerFactory)
{
services.AddSingleton<IConfiguration>(configuration);
services.AddScoped<IBasicAuthenticationService, BasicAuthenticationService>();
services.AddScoped<IMessagingService>(s => new MessagingServices(configuration, loggerFactory.CreateLogger<MessagingServices>()));
services.AddHostedService<TimedHostedService>();
}
}
在无法进行DI的情况下访问配置的小巧手。
public static class APIConfiguration
{
public static IConfiguration Current { get; set; }
}
答案 0 :(得分:5)
我尝试了这个,对我很有帮助。
private static bool HasAllowAnonymous(AuthorizationFilterContext context)
{
var filters = context.Filters;
for (var i = 0; i < filters.Count; i++)
{
if (filters[i] is IAllowAnonymousFilter)
{
return true;
}
}
// When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
// were discovered on controllers and actions. To maintain compat with 2.x,
// we'll check for the presence of IAllowAnonymous in endpoint metadata.
var endpoint = context.HttpContext.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
return true;
}
return false;
}