ASP.NET Core Web API Authentication

后端 未结 9 749
深忆病人
深忆病人 2020-12-04 04:58

I\'m struggling with how to set up authentication in my web service. The service is build with the ASP.NET Core web api.

All my clients (WPF applications) should us

相关标签:
9条回答
  • 2020-12-04 05:22

    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)]

    0 讨论(0)
  • 2020-12-04 05:24

    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;
      }
      
    0 讨论(0)
  • 2020-12-04 05:28

    You can use an 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();
        }
    }
    

    and add the attribute to your controller

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

    0 讨论(0)
  • 2020-12-04 05:31

    You can implement a middleware which handles Basic authentication.

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

    This code is written in a beta version of asp.net core. Hope it helps.

    0 讨论(0)
  • 2020-12-04 05:32

    I think you can go with JWT (Json Web Tokens).

    First you need to install the package System.IdentityModel.Tokens.Jwt:

    $ dotnet add package System.IdentityModel.Tokens.Jwt
    

    You will need to add a controller for token generation and authentication like this one:

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

    After that update Startup.cs class to look like below:

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

    And that's it, what is left now is to put [Authorize] attribute on the Controllers or Actions you want.

    Here is a link of a complete straight forward tutorial.

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

    0 讨论(0)
  • 2020-12-04 05:33

    I have implemented BasicAuthenticationHandler for basic authentication so you can use it with standart attributes Authorize and 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;
        }
    }
    

    Registration at Startup.cs - app.UseMiddleware<BasicAuthenticationMiddleware>();. With this code, you can restrict any controller with standart attribute Autorize:

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

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

    0 讨论(0)
提交回复
热议问题