developer tip

ASP.NET Core 웹 API 인증

copycodes 2020. 10. 31. 09:50
반응형

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

and use attribute AllowAnonymous if you apply authorize filter on application level.


In this public Github repo https://github.com/boskjoett/BasicAuthWebApi you can see a simple example of a ASP.NET Core 2.2 web API with endpoints protected by Basic Authentication.


As rightly said by previous posts, one of way is to implement a custom basic authentication middleware. I found the best working code with explanation in this blog: Basic Auth with custom middleware

I referred the same blog but had to do 2 adaptations:

  1. While adding the middleware in startup file -> Configure function, always add custom middleware before adding app.UseMvc().
  2. While reading the username, password from appsettings.json file, add static read only property in Startup file. Then read from appsettings.json. Finally, read the values from anywhere in the project. Example:

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

ASP.NET Core 2.0 with Angular

https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login

Make sure to use type of authentication filter

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

참고URL : https://stackoverflow.com/questions/38977088/asp-net-core-web-api-authentication

반응형