ASP.NET Core Web API Kimlik Doğrulaması


98

Web hizmetimde kimlik doğrulamayı nasıl ayarlayacağım konusunda zorlanıyorum. Hizmet, ASP.NET Core web api ile oluşturulmuştur.

Tüm müşterilerim (WPF uygulamaları) web hizmeti işlemlerini çağırmak için aynı kimlik bilgilerini kullanmalıdır.

Biraz araştırmadan sonra, temel kimlik doğrulamasını buldum - HTTP isteğinin başlığında bir kullanıcı adı ve şifre gönderdim. Ancak saatler süren araştırmalardan sonra, ASP.NET Core'da temel kimlik doğrulamanın gitmenin yolu olmadığını düşünüyorum.

Bulduğum kaynakların çoğu, OAuth veya başka bir ara yazılım kullanarak kimlik doğrulaması uyguluyor. Ancak bu benim senaryom için aşırı büyük görünüyor ve ASP.NET Core'un Kimlik bölümünü kullanıyor.

Öyleyse amacıma ulaşmanın doğru yolu nedir - bir ASP.NET Core web hizmetinde kullanıcı adı ve parola ile basit kimlik doğrulama?

Şimdiden teşekkürler!

Yanıtlar:


75

Temel kimlik doğrulamasını işleyen bir ara yazılım uygulayabilirsiniz.

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);
}

Bu kod, asp.net çekirdeğinin beta sürümünde yazılmıştır. Umarım yardımcı olur.


1
Cevabınız için teşekkürler! Tam olarak aradığım şey buydu - temel kimlik doğrulama için basit bir çözüm.
Felix

1
Bu kodda, kimlik bilgileri dizisinin kullanımından kaynaklanan bir hata var. Bölme (':') - iki nokta üst üste içeren parolaları doğru şekilde işlemez. Felix'in cevabındaki kod bu sorundan muzdarip değildir.
Phil Dennis

111

Şimdi, doğru yöne yönlendirildikten sonra, işte tam çözümüm:

Bu, gelen her istekte yürütülen ve isteğin doğru kimlik bilgilerine sahip olup olmadığını kontrol eden ara katman sınıfıdır. Kimlik bilgisi yoksa veya yanlışsa, hizmet hemen 401 Yetkisiz hatasıyla yanıt verir .

public class AuthenticationMiddleware
{
    private readonly RequestDelegate _next;

    public AuthenticationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        string authHeader = context.Request.Headers["Authorization"];
        if (authHeader != null && authHeader.StartsWith("Basic"))
        {
            //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);

            if(username == "test" && password == "test" )
            {
                await _next.Invoke(context);
            }
            else
            {
                context.Response.StatusCode = 401; //Unauthorized
                return;
            }
        }
        else
        {
            // no authorization header
            context.Response.StatusCode = 401; //Unauthorized
            return;
        }
    }
}

Ara yazılım uzantısı, hizmet Başlangıç ​​sınıfının Yapılandırma yönteminde çağrılmalıdır

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseMiddleware<AuthenticationMiddleware>();

    app.UseMvc();
}

Ve hepsi bu! :)

.Net Core ve kimlik doğrulama için çok iyi bir ara katman yazılımı kaynağı burada bulunabilir: https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/


4
Tam çözümü gönderdiğiniz için teşekkür ederiz. Ancak, 'context.Response.Headers.Add ("WWW-Authenticate", "Basic realm = \" realm \ "");' satırını eklemem gerekiyordu. tarayıcının kimlik bilgileri istemesini sağlamak için 'yetkilendirme başlığı yok' bölümüne.
m0n0ph0n

Bu kimlik doğrulama ne kadar güvenli? Ya birisi istek başlığını koklarsa ve kullanıcı adını / parolayı alırsa?
Bewar Salah

6
@BewarSalah bu tür bir çözümü https üzerinden sunmalısınız
wal

2
Bazı denetleyiciler anonim olarak izin vermelidir. Bu ara yazılım çözümü bu durumda başarısız olur çünkü her istekte yetkilendirme başlığını kontrol eder.
Karthik

28

Bunu yalnızca belirli denetleyiciler için kullanmak için, örneğin şunu kullanın:

app.UseWhen(x => (x.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase)), 
            builder =>
            {
                builder.UseMiddleware<AuthenticationMiddleware>();
            });

22

JWT (Json Web Token) ile gidebileceğinizi düşünüyorum.

Öncelikle System.IdentityModel.Tokens.Jwt paketini kurmanız gerekir:

$ dotnet add package System.IdentityModel.Tokens.Jwt

Belirteç oluşturma ve kimlik doğrulama için aşağıdaki gibi bir denetleyici eklemeniz gerekecek:

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("Secret Key You Devise")),
                                         SecurityAlgorithms.HmacSha256)),
            new JwtPayload(claims));

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

Bundan sonra Startup.cs sınıfını aşağıdaki gibi görünecek şekilde güncelleyin:

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("Secret Key You Devise")),
                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();
    }
}

İşte bu, şimdi geriye kalan [Authorize], istediğiniz Denetleyicilere veya Eylemlere öznitelik koymaktır .

İşte eksiksiz ve basit bir öğreticinin bağlantısı.

http://www.blinkingcaret.com/2017/09/06/secure-web-api-in-asp-net-core/


9

BasicAuthenticationHandlerStandart özniteliklerle kullanabilmeniz için temel kimlik doğrulama için uyguladım Authorizeve 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(':', StringComparison.OrdinalIgnoreCase);

            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;
    }
}

Startup.cs'de Kayıt - app.UseMiddleware<BasicAuthenticationMiddleware>();. Bu kodla, herhangi bir denetleyiciyi standart öznitelik Otorize ile sınırlayabilirsiniz:

[Authorize(ActiveAuthenticationSchemes = "Basic")]
[Route("api/[controller]")]
public class ValuesController : Controller

ve AllowAnonymousuygulama düzeyinde yetkilendirme filtresi uygularsanız özniteliği kullanın .


1
Kodunuzu kullandım, ancak Yetkilendir (ActiveAuthenticationSchemes = "Temel")] ayarlı olup olmadığına bakılmaksızın her çağrıda ara yazılım etkinleştirilir ve istenmediğinde de her denetleyicinin doğrulanmasıyla sonuçlanır.
CSharper

Bu yanıtı beğendim
KTOV

1
Burada örnek çalışma: jasonwatmore.com/post/2018/09/08/...
bside

Bence bu, çözümde standart authorize / allowanonymous özniteliklerini kullanmanıza izin verdiği için, bu sorunun cevabıdır. Bunun yanı sıra, projenin
ilerleyen safhalarında

0

Bu genel Github deposunda https://github.com/boskjoett/BasicAuthWebApi , Temel Kimlik Doğrulaması ile korunan uç noktalara sahip basit bir ASP.NET Core 2.2 web API örneği görebilirsiniz.


Denetleyicinizde Authenticated Identity'yi (SecureValuesController) kullanmak istiyorsanız, Request.User nesnesi boş olduğu için bir bilet oluşturmak yeterli değildir. Yine de bu ClaimsPrincipal'i AuthenticationHandler'daki mevcut Context'e atamamız gerekiyor mu? Daha eski WebApi'de bunu başardık ...
pseabury

0

Önceki yayınlarda doğru bir şekilde söylendiği gibi, yollardan biri özel bir temel kimlik doğrulama ara yazılımını uygulamaktır. Bu blogda açıklamayla birlikte en iyi çalışan kodu buldum: Özel ara yazılım ile Temel Kimlik Doğrulama

Aynı bloga atıfta bulundum ancak 2 uyarlama yapmak zorunda kaldım:

  1. Ara katman yazılımını başlangıç ​​dosyasına eklerken -> Yapılandır işlevini, uygulama eklemeden önce her zaman özel ara yazılım ekleyin.Mvc () kullanın.
  2. Appsettings.json dosyasındaki kullanıcı adını, şifreyi okurken, Başlangıç ​​dosyasına statik salt okunur özelliği ekleyin. Ardından appsettings.json'dan okuyun. Son olarak, değerleri projenin herhangi bir yerinden okuyun. Misal:

    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;
    }
    

0

Kullanabilirsin ActionFilterAttribute

public class BasicAuthAttribute : ActionFilterAttribute
{
    public string BasicRealm { get; set; }
    protected NetworkCredential Nc { get; set; }

    public BasicAuthAttribute(string user,string pass)
    {
        this.Nc = new NetworkCredential(user,pass);
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var req = filterContext.HttpContext.Request;
        var auth = req.Headers["Authorization"].ToString();
        if (!String.IsNullOrEmpty(auth))
        {
            var cred = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(auth.Substring(6)))
                .Split(':');
            var user = new {Name = cred[0], Pass = cred[1]};
            if (user.Name == Nc.UserName && user.Pass == Nc.Password) return;
        }

        filterContext.HttpContext.Response.Headers.Add("WWW-Authenticate",
            String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Ryadel"));
        filterContext.Result = new UnauthorizedResult();
    }
}

ve özelliği denetleyicinize ekleyin

[BasicAuth("USR", "MyPassword")]


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.