Je ne parviens pas à configurer l'authentification dans mon service Web. Le service est créé avec l’API Web ASP.NET Core.
Tous mes clients (applications WPF) doivent utiliser les mêmes informations d'identification pour appeler les opérations du service Web.
Après quelques recherches, je suis arrivé à l’authentification de base - en envoyant un nom d’utilisateur et un mot de passe dans l’en-tête de la requête HTTP . Coeur.
La plupart des ressources que j'ai trouvées implémentent l'authentification en utilisant OAuth ou un autre middleware. Mais cela semble être surdimensionné pour mon scénario, ainsi que pour l'utilisation de la partie Identité d'ASP.NET Core.
Quel est donc le bon moyen d’atteindre mon objectif - une authentification simple avec un nom d’utilisateur et un mot de passe dans un service Web ASP.NET Core?
Merci d'avance!
Vous pouvez implémenter un middleware qui gère l'authentification de base.
public async Task Invoke(HttpContext context)
{
var authHeader = context.Request.Headers.Get("Authorization");
if (authHeader != null && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
var token = authHeader.Substring("Basic ".Length).Trim();
System.Console.WriteLine(token);
var credentialstring = Encoding.UTF8.GetString(Convert.FromBase64String(token));
var credentials = credentialstring.Split(':');
if(credentials[0] == "admin" && credentials[1] == "admin")
{
var claims = new[] { new Claim("name", credentials[0]), new Claim(ClaimTypes.Role, "Admin") };
var identity = new ClaimsIdentity(claims, "Basic");
context.User = new ClaimsPrincipal(identity);
}
}
else
{
context.Response.StatusCode = 401;
context.Response.Headers.Set("WWW-Authenticate", "Basic realm=\"dotnetthoughts.net\"");
}
await _next(context);
}
Ce code est écrit dans une version bêta de asp.net core. J'espère que ça aide.
Pour utiliser ceci uniquement pour des contrôleurs spécifiques, par exemple, utilisez ceci:
app.UseWhen(x => (x.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase)),
builder =>
{
builder.UseMiddleware<AuthenticationMiddleware>();
});
Je pense que vous pouvez aller avec JWT (Json Web Tokens).
Vous devez d’abord installer le package System.IdentityModel.Tokens.Jwt:
$ dotnet add package System.IdentityModel.Tokens.Jwt
Vous devrez ajouter un contrôleur pour la génération et l’authentification de jetons, comme celui-ci:
public class TokenController : Controller
{
[Route("/token")]
[HttpPost]
public IActionResult Create(string username, string password)
{
if (IsValidUserAndPasswordCombination(username, password))
return new ObjectResult(GenerateToken(username));
return BadRequest();
}
private bool IsValidUserAndPasswordCombination(string username, string password)
{
return !string.IsNullOrEmpty(username) && username == password;
}
private string GenerateToken(string username)
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds().ToString()),
};
var token = new JwtSecurityToken(
new JwtHeader(new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes("H38DLSIEKD8EKDOS")),
SecurityAlgorithms.HmacSha256)),
new JwtPayload(claims));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
Après cette mise à jour, la classe Startup.cs se présentera comme suit:
namespace WebAPISecurity
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = "JwtBearer";
options.DefaultChallengeScheme = "JwtBearer";
})
.AddJwtBearer("JwtBearer", jwtBearerOptions =>
{
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("H38DLSIEKD8EKDOS")),
ValidateIssuer = false,
//ValidIssuer = "The name of the issuer",
ValidateAudience = false,
//ValidAudience = "The name of the audience",
ValidateLifetime = true, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
}
Et voilà, il ne reste plus qu'à mettre l'attribut [Authorize]
sur les contrôleurs ou les actions de votre choix.
Voici un lien vers un tutoriel simple et complet.
http://www.blinkingcaret.com/2017/09/06/secure-web-api-in-asp-net-core/
J'ai implémenté BasicAuthenticationHandler
pour l'authentification de base afin que vous puissiez l'utiliser avec les attributs standart Authorize
et AllowAnonymous
.
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authHeader = (string)this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
//you also can use this.Context.Authentication here
if (username == "test" && password == "test")
{
var user = new GenericPrincipal(new GenericIdentity("User"), null);
var ticket = new AuthenticationTicket(user, new AuthenticationProperties(), Options.AuthenticationScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
else
{
return Task.FromResult(AuthenticateResult.Fail("No valid user."));
}
}
this.Response.Headers["WWW-Authenticate"]= "Basic realm=\"yourawesomesite.net\"";
return Task.FromResult(AuthenticateResult.Fail("No credentials."));
}
}
public class BasicAuthenticationMiddleware : AuthenticationMiddleware<BasicAuthenticationOptions>
{
public BasicAuthenticationMiddleware(
RequestDelegate next,
IOptions<BasicAuthenticationOptions> options,
ILoggerFactory loggerFactory,
UrlEncoder encoder)
: base(next, options, loggerFactory, encoder)
{
}
protected override AuthenticationHandler<BasicAuthenticationOptions> CreateHandler()
{
return new BasicAuthenticationHandler();
}
}
public class BasicAuthenticationOptions : AuthenticationOptions
{
public BasicAuthenticationOptions()
{
AuthenticationScheme = "Basic";
AutomaticAuthenticate = true;
}
}
Enregistrement à Startup.cs - app.UseMiddleware<BasicAuthenticationMiddleware>();
. Avec ce code, vous pouvez restreindre n'importe quel contrôleur avec l'attribut standart Autorize:
[Authorize(ActiveAuthenticationSchemes = "Basic")]
[Route("api/[controller]")]
public class ValuesController : Controller
et utilisez l'attribut AllowAnonymous
si vous appliquez le filtre d'autorisation au niveau de l'application.
ASP.NET Core 2.0 avec Angular
Assurez-vous d'utiliser le type de filtre d'authentification
[Authorize (AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Dans ce Github repo https://github.com/boskjoett/BasicAuthWebApi .__ public, vous pouvez voir un exemple simple d'une API Web ASP.NET Core 2.2 avec des points de terminaison protégés par l'authentification de base.
Comme mentionné à juste titre dans les publications précédentes, l’un des moyens consiste à mettre en œuvre un middleware d’authentification de base personnalisé. J'ai trouvé le meilleur code de travail avec explication dans ce blog: Autorisation de base avec middleware personnalisé
J'ai référé le même blog mais j'ai dû faire 2 adaptations:
Lors de la lecture du nom d'utilisateur, mot de passe du fichier appsettings.json, ajoutez une propriété statique en lecture seule dans le fichier de démarrage. Puis lisez à partir de appsettings.json. Enfin, lisez les valeurs de n’importe où dans le projet. Exemple:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public static string UserNameFromAppSettings { get; private set; }
public static string PasswordFromAppSettings { get; private set; }
//set username and password from appsettings.json
UserNameFromAppSettings = Configuration.GetSection("BasicAuth").GetSection("UserName").Value;
PasswordFromAppSettings = Configuration.GetSection("BasicAuth").GetSection("Password").Value;
}