.net framework use of Identity

Recall that two days before doing the project, the certification has been relatively headache place.
So taking the time to read some of the content, starting form authentication, see the identity, the process is just a simple browser, but before the concept can be considered to have some deepened.

In fact, these two genius to know, form authentication is already a thing of the past in .net, Identity has been replaced. After Nonetheless, I read form authentication, certification of the whole process is also have some understanding, but here is get down some identity-related content.

Owin

Owin identity is based on the framework. Owin actually a specification to achieve its .net, called kantana, but in fact they use, I think that basically is interconnected.

Owin no longer rely on IIS request pipeline, but a registration mechanism to customize the components of the individual that is already similar to the practice of the .net core.

Owin and IIS

Owin can run on IIS, you can also run independently, Owin operate independently with its own SelfHost components, when IIS-based, Owin dependent Microsoft.Owin.Host.SystemWeblibraries complete the integration of IIS.

The IIS Global.asaxand Owin of StartUpcallbacks also be present at the same time, it will be called.
If integrated into the IIS, Owin the initialization call, almost triggered after Application_Start event.

Owin starting class

Owin the pipeline is arranged in the starting class Configuration process.
There are two configurations, one is specified in Web.config AppSettings node:

<add key="owin:AppStartup" value="起始类" />

Another property configuration by, containing Configurationlabeling properties on the class methods

[assembly: OwinStartup(typeof(StartupDemo.TestStartup))]

Identity

Identity replace the original Form Authentication to some extent, but if you do not use Owin, I still have to understand the use of Form Authentication.

Identity is actually in response to the emergence of third-party certification under the network environment (google, fb, etc.), two-factor authentication (2FA) demand, the traditional way of Form are less likely to expand.

Reference to this part of the SO to some extent, the answer to this question

Identity components increase

Apart Owin dependence, Identity also on EntityFramework, require several components Nuget:

  • Microsoft.AspNet.Identity.Owin
  • Microsoft.AspNet.Identity.EntityFramework
  • Microsoft.Owin.Host.SystemWeb

If you need to use MySql, also you need to install

  • MySql.Data.Entity

Configuration and initialization

Profiles

Web.config configuration needs to add some content

Entity back to this part of the automatic increase in the installation of the Entity, in fact, do not have to manually modify:

<configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>

In particular, I use is based on Owin Startup Configuration, so in the AppSettings node increases my portal arrangement

<add key="owin:AppStartup" value="Demo.WEBUI.IdentityConfig" />

Identity by curing Entity user information, it is necessary to configure the ConnectionString Entity used, this is part of a common convention .net:

  <connectionStrings>
    <add name="DefaultConnection" connectionString="server=localhost;database=dbname;uid=root;pwd=123456" providerName="MySql.Data.MySqlClient" />
  </connectionStrings>

Entity increase in support for MySql, usually installed by default, it is the SqlServer:

<entityFramework>
    <defaultConnectionFactory type="MySql.Data.Entity.MySqlConnectionFactory, MySql.Data.Entity.EF6" />
    <providers>
      <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.10.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>

Also Entity, increase DbProvider, this node does not find a better explanation, but not without words.

  <system.data>
    <DbProviderFactories>
      <remove invariant="MySql.Data.MySqlClient"></remove>
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.10.8.0" />
    </DbProviderFactories>
  </system.data>

Auxiliary class

user

Identity of the user class, can be used directly IdentityUser, can also be extended from his foundation, add custom fields, which will join the User table, I've added three fields.

 public class ApplicationUser : IdentityUser
    {
        public virtual DateTime? LastLoginTime { get; set; }
        public virtual DateTime? RegistrationTime { get; set; }

        public virtual bool IsEnabled { get; set; }

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }
    }

User Management

Here you have configured user authentication settings, such as password strength, cookie expiration time

    public class ApplicationUserManager : UserManager<ApplicationUser>
    {
        private IUserStore<ApplicationUser> _store;

        public ApplicationUserManager(IUserStore<ApplicationUser> store)
            : base(store)
        {
            _store = store;
        }

        public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
        {
            var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
            // Configure validation logic for usernames
            manager.UserValidator = new UserValidator<ApplicationUser>(manager)
            {
                AllowOnlyAlphanumericUserNames = true,
                RequireUniqueEmail = false
            };

            // Configure validation logic for passwords
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = 4,
                RequireNonLetterOrDigit = false,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false,
            };

            // Configure user lockout defaults
            manager.UserLockoutEnabledByDefault = false;
            //manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
            //manager.MaxFailedAccessAttemptsBeforeLockout = 5;

            var dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider =
                    new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
            }
            return manager;
        }
    }

Log Management

User login method calls, which will depend on the previous stepApplicationUserManager

    public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
    {
        public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
            : base(userManager, authenticationManager)
        {
        }

        public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
        {
            return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
        }

        public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
        {
            return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
        }
    }

Role Management

Here I did not do more extensions. Identity itself can not role-based, but on Claim permissions control.
Therefore, the role of management is not required.

  public class ApplicationRoleManager : RoleManager<IdentityRole, string>
    {
        public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
            : base(roleStore)
        {
        }

        public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
        {
            return new ApplicationRoleManager(new RoleStore<IdentityRole, string, IdentityUserRole>(context.Get<ApplicationDbContext>()));
        }
    }

Initialization class

If the new project with a .net mvc authentication will automatically add the initial class.

I Identity add on an existing project, where the initialization function to initialize class from copy vs created automatically and do not need to delete some of the functions (only local login).

        public void Configuration(IAppBuilder app)
        {
            // Configure the db context, user manager and signin manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
            app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            // Configure the sign in cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Login/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });
            //app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
            //app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

            // Enables the application to remember the second login verification factor such as phone or email.
            // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
            // This is similar to the RememberMe option when you log in.
            //app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
        }

use

The main focus ApplicationUserManager, ApplicationSignInManageruser registration login and other operations around the ApplicationRoleManageruser rights management.

registered

The basic logic or from registered vs the default framework, but adds some customized features.

Here I increased user role settings and define the user first registered as an administrator, then for the average user.
Applications, other places will come to judge the operation can be carried out through user roles.

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    var user = new ApplicationUser
                    {
                        UserName = model.UserName,
                        Email = model.Password,
                        IsEnabled = false, //default disabed
                        LastLoginTime = DateTime.Now,
                        RegistrationTime = DateTime.Now
                    };
                    var result = await UserManager.CreateAsync(user, model.Password);
                    if (result.Succeeded)
                    {
                        //init roles
                        if (!RoleManager.Roles.Any())
                        {
                            await RoleManager.CreateAsync(new IdentityRole(UserRole.NORMAL));
                            await RoleManager.CreateAsync(new IdentityRole(UserRole.ADMINISTRATOR));
                        }

                        var adminRoleId = (await RoleManager.FindByNameAsync(UserRole.ADMINISTRATOR)).Id;
                        var hasAdmin = UserManager.Users.Any(p => p.Roles.Any(x => x.RoleId == adminRoleId));
                        var userRole = UserRole.NORMAL;
                        if (!hasAdmin)
                        {
                            userRole = UserRole.ADMINISTRATOR;
                            //make the first admin enabled
                            user.IsEnabled = true;
                            await UserManager.UpdateAsync(user);
                        }

                        await UserManager.AddToRoleAsync(user.Id, userRole);

                        //let the user manual log in
                        //await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

                        // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
                        // Send an email with this link
                        // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                        // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                        // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

                        return RedirectToAction("Login", "Login");
                    }
                    AddErrors(result);
                }
                catch (System.Data.Entity.Validation.DbEntityValidationException e)
                {
                    Console.WriteLine(e.ToString());
                    throw e;
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

log in

When I define a user adds custom fields if the user login time and user enabled.
Here the two fields have made use of.

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            var user = await UserManager.FindByNameAsync(model.UserName);
            if (null == user)
            {
                ModelState.AddModelError("", "登录失败.");
                return View(model);
            }

            if ((!await UserManager.IsInRoleAsync(user.Id, UserRole.ADMINISTRATOR)) && !user.IsEnabled)
            {
                ModelState.AddModelError("", "账户未启用,请联系管理员.");
                return View(model);
            }

            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, change to shouldLockout: true
            var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
            switch (result)
            {
                case SignInStatus.Success:
                    user.LastLoginTime = DateTime.Now;
                    await UserManager.UpdateAsync(user);
                    return RedirectToLocal(returnUrl);

                default:
                    ModelState.AddModelError("", "登录失败.");
                    return View(model);
            }
        }

Some extensions use

reset Password

More secure password reset logic is by mail, phone, etc., but I was in the system, the easiest way is to use the administrator set directly.

It is more common to token may be sent to the user by means of connection, by connecting the user to manually change the password.

        [HttpPost]
        public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            if (!IsAdmin && !model.UserName.Equals(User.Identity.Name))
            {
                ModelState.AddModelError("", "无效的用户名");
                return View();
            }

            var user = await UserManager.FindByNameAsync(model.UserName);
            if (user == null)
            {
                return View();
            }

            var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
            var result = await UserManager.ResetPasswordAsync(user.Id, token, model.Password);
            if (!result.Succeeded)
            {
                AddErrors(result);
            }
            else
            {
                ViewBag.msg = "修改成功!";
            }

            return View();
        }

Access control

In fact, based on Claim by the way, but the system is relatively simple, controlled by Claim somewhat complicated, so I used the Role direct way.

There are two ways, first is the User Properties Controller / WebViewPage the inside by a User.IsInRole(UserRole.ADMINISTRATOR);method for determining user permissions, here is the incoming Role name, not RoleId.

Another is the ApplicationUserManager UserManager.GetRoles(user.Id)method, you can get all the Role Name user, and then determines a desired one by Role.

problem

MySql connection is unsuccessful

Sometimes NuGet 8.xx version will be installed for the system MySql.Data, search, I found that many people encounter problems in this version, downgrade to 6.xx version.

RememberMe function

This problem has not been resolved, when a user logs calls


            var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);

Here's third argument is valid logon settings, set theory false, the limitation period is Cookie CurrentSession, and truethen, will be a long period of time.

In fact we found that passed true, the expiration time is 15 days (this relatively normal); and passed false, would be valid N/A, never expire. There is no way to solve the discovery.

Guess you like

Origin www.cnblogs.com/mosakashaka/p/12608845.html