Custom Authentication in ASP.Net-Core

后端 未结 4 1169
暖寄归人
暖寄归人 2020-11-28 19:26

I am working on a web app that needs to integrate with an existing user database. I would still like to use the [Authorize] attributes, but I don\'t want to use

相关标签:
4条回答
  • 2020-11-28 19:32

    Creating custom authentication in ASP.NET Core can be done in a variety of ways. If you want to build off existing components (but don't want to use identity), checkout the "Security" category of docs on docs.asp.net. https://docs.asp.net/en/latest/security/index.html

    Some articles you might find helpful:

    Using Cookie Middleware without ASP.NET Identity

    Custom Policy-Based Authorization

    And of course, if that fails or docs aren't clear enough, the source code is at https://github.com/dotnet/aspnetcore/tree/master/src/Security which includes some samples.

    0 讨论(0)
  • 2020-11-28 19:41

    From what I learned after several days of research, Here is the Guide for ASP .Net Core MVC 2.x Custom User Authentication

    In Startup.cs :

    Add below lines to ConfigureServices method :

    public void ConfigureServices(IServiceCollection services)
    {
    
    services.AddAuthentication(
        CookieAuthenticationDefaults.AuthenticationScheme
    ).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
        options =>
        {
            options.LoginPath = "/Account/Login";
            options.LogoutPath = "/Account/Logout";
        });
    
        services.AddMvc();
    
        // authentication 
        services.AddAuthentication(options =>
        {
           options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        });
    
        services.AddTransient(
            m => new UserManager(
                Configuration
                    .GetValue<string>(
                        DEFAULT_CONNECTIONSTRING //this is a string constant
                    )
                )
            );
         services.AddDistributedMemoryCache();
    }
    

    keep in mind that in above code we said that if any unauthenticated user requests an action which is annotated with [Authorize] , they well force redirect to /Account/Login url.

    Add below lines to Configure method :

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler(ERROR_URL);
        }
         app.UseStaticFiles();
         app.UseAuthentication();
         app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: DEFAULT_ROUTING);
        });
    }
    

    Create your UserManager class that will also manage login and logout. it should look like below snippet (note that i'm using dapper):

    public class UserManager
    {
        string _connectionString;
    
        public UserManager(string connectionString)
        {
            _connectionString = connectionString;
        }
    
        public async void SignIn(HttpContext httpContext, UserDbModel user, bool isPersistent = false)
        {
            using (var con = new SqlConnection(_connectionString))
            {
                var queryString = "sp_user_login";
                var dbUserData = con.Query<UserDbModel>(
                    queryString,
                    new
                    {
                        UserEmail = user.UserEmail,
                        UserPassword = user.UserPassword,
                        UserCellphone = user.UserCellphone
                    },
                    commandType: CommandType.StoredProcedure
                ).FirstOrDefault();
    
                ClaimsIdentity identity = new ClaimsIdentity(this.GetUserClaims(dbUserData), CookieAuthenticationDefaults.AuthenticationScheme);
                ClaimsPrincipal principal = new ClaimsPrincipal(identity);
    
                await httpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
            }
        }
    
        public async void SignOut(HttpContext httpContext)
        {
            await httpContext.SignOutAsync();
        }
    
        private IEnumerable<Claim> GetUserClaims(UserDbModel user)
        {
            List<Claim> claims = new List<Claim>();
    
            claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id().ToString()));
            claims.Add(new Claim(ClaimTypes.Name, user.UserFirstName));
            claims.Add(new Claim(ClaimTypes.Email, user.UserEmail));
            claims.AddRange(this.GetUserRoleClaims(user));
            return claims;
        }
    
        private IEnumerable<Claim> GetUserRoleClaims(UserDbModel user)
        {
            List<Claim> claims = new List<Claim>();
    
            claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id().ToString()));
            claims.Add(new Claim(ClaimTypes.Role, user.UserPermissionType.ToString()));
            return claims;
        }
    }
    

    Then maybe you have an AccountController which has a Login Action that should look like below :

    public class AccountController : Controller
    {
        UserManager _userManager;
    
        public AccountController(UserManager userManager)
        {
            _userManager = userManager;
        }
    
        [HttpPost]
        public IActionResult LogIn(LogInViewModel form)
        {
            if (!ModelState.IsValid)
                return View(form);
             try
            {
                //authenticate
                var user = new UserDbModel()
                {
                    UserEmail = form.Email,
                    UserCellphone = form.Cellphone,
                    UserPassword = form.Password
                };
                _userManager.SignIn(this.HttpContext, user);
                 return RedirectToAction("Search", "Home", null);
             }
             catch (Exception ex)
             {
                ModelState.AddModelError("summary", ex.Message);
                return View(form);
             }
        }
    }
    

    Now you are able to use [Authorize] annotation on any Action or Controller.

    Feel free to comment any questions or bug's.

    0 讨论(0)
  • 2020-11-28 19:46

    @Manish Jain, I suggest to implement the method with boolean return:

    public class UserManager
    {
    
        // Additional code here...            
    
        public async Task<bool> SignIn(HttpContext httpContext, UserDbModel user)
        {
            // Additional code here...            
    
            // Here the real authentication against a DB or Web Services or whatever 
            if (user.Email != null)
                return false;                    
    
            ClaimsIdentity identity = new ClaimsIdentity(this.GetUserClaims(dbUserData), CookieAuthenticationDefaults.AuthenticationScheme);
            ClaimsPrincipal principal = new ClaimsPrincipal(identity);
    
            // This is for give the authentication cookie to the user when authentication condition was met
            await httpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
            return true;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 19:53

    I would like to add something to brilliant @AmiNadimi answer for everyone who going implement his solution in .NET Core 3:

    First of all, you should change signature of SignIn method in UserManager class from:

    public async void SignIn(HttpContext httpContext, UserDbModel user, bool isPersistent = false)
    

    to:

    public async Task SignIn(HttpContext httpContext, UserDbModel user, bool isPersistent = false)
    

    It's because you should never use async void, especially if you work with HttpContext. Source: Microsoft Docs

    The last, but not least, your Configure() method in Startup.cs should contains app.UseAuthorization and app.UseAuthentication in proper order:

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseAuthentication();
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
    
    0 讨论(0)
提交回复
热议问题