[c#] ASP.NET Core 웹 API 인증

내 웹 서비스에서 인증을 설정하는 방법에 어려움을 겪고 있습니다. 이 서비스는 ASP.NET Core 웹 API로 빌드됩니다.

내 모든 클라이언트 (WPF 애플리케이션)는 동일한 자격 증명을 사용하여 웹 서비스 작업을 호출해야합니다.

몇 가지 조사 끝에 HTTP 요청 헤더에 사용자 이름과 비밀번호를 보내는 기본 인증을 생각해 냈습니다. 그러나 몇 시간의 연구 끝에 기본 인증이 ASP.NET Core로 이동하는 방법이 아닌 것 같습니다.

내가 찾은 대부분의 리소스는 OAuth 또는 다른 미들웨어를 사용하여 인증을 구현하고 있습니다. 그러나 그것은 ASP.NET Core의 ID 부분을 사용하는 것뿐만 아니라 내 시나리오에 비해 너무 큰 것 같습니다.

그렇다면 ASP.NET Core 웹 서비스에서 사용자 이름과 암호를 사용한 간단한 인증이라는 목표를 달성하는 올바른 방법은 무엇입니까?

미리 감사드립니다!



답변

기본 인증을 처리하는 미들웨어를 구현할 수 있습니다.

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

이 코드는 asp.net core의 베타 버전으로 작성되었습니다. 도움이 되었기를 바랍니다.


답변

이제 올바른 방향을 가리키고 나면 완전한 솔루션이 있습니다.

이것은 들어오는 모든 요청에서 실행되고 요청에 올바른 자격 증명이 있는지 확인하는 미들웨어 클래스입니다. 자격 증명이 없거나 잘못된 경우 서비스는 즉시 401 Unauthorized 오류로 응답 합니다.

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

미들웨어 확장은 서비스 Startup 클래스의 Configure 메서드에서 호출해야합니다.

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

    app.UseMiddleware<AuthenticationMiddleware>();

    app.UseMvc();
}

그리고 그게 전부입니다! 🙂

.Net Core 및 인증의 미들웨어에 대한 매우 좋은 리소스는 https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/ 에서 찾을 수 있습니다.


답변

예를 들어 특정 컨트롤러에만 이것을 사용하려면 다음을 사용하십시오.

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


답변

JWT (Json Web Tokens)로 갈 수 있다고 생각합니다.

먼저 System.IdentityModel.Tokens.Jwt 패키지를 설치해야합니다.

$ dotnet add package System.IdentityModel.Tokens.Jwt

다음과 같이 토큰 생성 및 인증을위한 컨트롤러를 추가해야합니다.

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

그 후 Startup.cs 클래스를 다음과 같이 업데이트하십시오.

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

이제 남은 것은 [Authorize]원하는 컨트롤러 또는 액션 에 속성을 추가하는 것입니다.

여기에 완전한 간단한 튜토리얼 링크가 있습니다.

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


답변

BasicAuthenticationHandler기본 인증 을 구현 하여 표준 속성 AuthorizeAllowAnonymous.

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- app.UseMiddleware<BasicAuthenticationMiddleware>();. 이 코드를 사용하면 표준 속성 Autorize로 모든 컨트롤러를 제한 할 수 있습니다.

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

AllowAnonymous응용 프로그램 수준에서 권한 부여 필터를 적용하는 경우 속성을 사용합니다 .


답변

이 공개 Github 리포지토리
https://github.com/boskjoett/BasicAuthWebApi
에서 기본 인증으로 보호되는 엔드 포인트가있는 ASP.NET Core 2.2 웹 API의 간단한 예제를 볼 수 있습니다.


답변

이전 게시물에서 올바르게 언급했듯이 방법 중 하나는 사용자 지정 기본 인증 미들웨어를 구현하는 것입니다. 이 블로그에서 설명과 함께 가장 잘 작동하는 코드를 찾았습니다.
사용자 지정 미들웨어를 사용한 기본 인증

동일한 블로그를 참조했지만 두 가지 수정 작업을 수행해야했습니다.

  1. 시작 파일-> 구성 기능에 미들웨어를 추가하는 동안 app.UseMvc ()를 추가하기 전에 항상 사용자 정의 미들웨어를 추가하십시오.
  2. appsettings.json 파일에서 사용자 이름, 비밀번호를 읽는 동안 시작 파일에 정적 읽기 전용 속성을 추가합니다. 그런 다음 appsettings.json에서 읽습니다. 마지막으로 프로젝트의 어느 곳에서나 값을 읽습니다. 예:

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