1.Authentication verifies the user identity of the visitor. (Whether the user logged in successfully)
Whether the Authorization user identity has access rights to resources. (Whether the user has permission to access this address)
(1) Install the nuget package: ASP.NET Core Identity Entity Framework Core.
(2) Create three types:
public class MyUser:IdentityUser<long>//long is the primary key user table { }
public class MyRole: IdentityRole<long>//Role table { }
public class MyDbContext: IdentityDbContext<MyUser,MyRole,long>//Remember the three generics here { public MyDbContext(DbContextOptions<MyDbContext> options) :base(options) { } }
(3) Set the Identity framework in the program:
//Configure connection string service builder.Services.AddDbContext<MyDbContext>(opt => { string? sqlsr = builder.Configuration.GetSection("DBcontext").Value; opt.UseSqlServer(sqlsr); }); //Encryption service builder.Services.AddDataProtection(); //Configure user services builder.Services.AddIdentityCore<MyUser>(options => { options.Lockout.MaxFailedAccessAttempts = 5;//The account will be locked after five errors options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(0.005);//Set the account lock time options.Password.RequireDigit = false;//Whether there must be a number options.Password.RequireLowercase = false;//Whether there must be lowercase letters options.Password.RequireNonAlphanumeric = false;//Whether non-alphanumeric is required options.Password.RequireUppercase=false;//Whether uppercase letters are required options.Password.RequiredLength = 6;//Require password length options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;//Password recognition options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;//Email confirmation }); //The combination of identity service framework IdentityBuilder idBuilder=new IdentityBuilder(typeof(MyUser), typeof(MyRole),builder.Services); idBuilder.AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders() .AddUserManager<UserManager<MyUser>>().AddRoleManager<RoleManager<MyRole>>();
(4) It may not be recognized during database migration, so build a class that inherits the IDesignTimeDbContextFactory interface.
public class DbContextDesignTimeFactory : IDesignTimeDbContextFactory<MyDbContext> { public MyDbContext CreateDbContext(string[] args) { DbContextOptionsBuilder<MyDbContext> builder = new DbContextOptionsBuilder<MyDbContext>(); //Configure the connection string in the environment variable and obtain the connection string from the environment variable string? constr = Environment.GetEnvironmentVariable("haha").ToString(); builder.UseSqlServer(constr); return new MyDbContext(builder.Options); } }
(5) User login verification (if you need to use parameters, you can change it yourself)
[HttpPost] public async Task<ActionResult<string>> Test1() { if (await roleMger.RoleExistsAsync("admin") == false) { MyRole myRole = new MyRole { Name = "admin" }; var result = await roleMger.CreateAsync(myRole); if (!result.Succeeded) { return BadRequest("roleMger CreateAsync filed"); } } MyUser? user1 = await userMger.FindByNameAsync("mas"); if (user1 == null) { user1 = new MyUser { UserName = "mas" }; var result = await userMger.CreateAsync(user1, "123456"); if (!result.Succeeded) { return BadRequest("userMger CreateAsync filed"); } } if (!await userMger.IsInRoleAsync(user1, "mas")) { var result = await userMger.AddToRoleAsync(user1, "admin"); if (!result.Succeeded) { return BadRequest("userMger AddToRoleAsync filed"); } } return "ok"; }
(6) Detect logged in user information
[HttpPost] public async Task<ActionResult> CheckPwd(CheckPwdRequire require) { string username = require.User; string password = require.Password; var sue = await userMger.FindByNameAsync(username); if (sue == null) { if (webHostEnvironment.IsDevelopment()) { return BadRequest("Username input error"); } else { return BadRequest(); // safer } } if (await userMger.IsLockedOutAsync(sue)) { return BadRequest("The user is locked, the lock end time is " + sue.LockoutEnd); } if (await userMger.CheckPasswordAsync(sue, password)) { await userMger.ResetAccessFailedCountAsync(sue); //Recount lock if login is successful return Ok("Login successful"); } else { //Record login failure await userMger.AccessFailedAsync(sue); return BadRequest("Username or password is wrong"); } }
(7) Implement the function of resetting password
《1》Password reset and send verification code:
/// <summary> /// User password reset /// </summary> /// <param name="userName"></param> /// <returns></returns> [HttpPost] public async Task<ActionResult> RestPassword(string userName) { var user = await userMger.FindByNameAsync(userName); if (user == null) { return BadRequest("The user does not exist!"); } string token = await userMger.GeneratePasswordResetTokenAsync(user); Console.WriteLine($"The verification code is {token}"); return Ok(); }
《2》Get the 6-digit verification code in the console and change the password
/// <summary> /// Input and reset of verification code /// </summary> /// <param name="name"></param> /// <param name="token"></param> /// <param name="newPassword"></param> /// <returns></returns> [HttpPost] public async Task<ActionResult> RestPasswordnumber(string UserName, string token, string newPassword) { var user = await userMger.FindByNameAsync(UserName); if (user == null) { return BadRequest("The user does not exist!"); } var result = await userMger.ResetPasswordAsync(user, token, newPassword); if (result.Succeeded) { await userMger.ResetAccessFailedCountAsync(user); return Ok("Password reset successful"); } else { await userMger.AccessFailedAsync(user); return Ok("Password reset failed"); } }
JWT module: Save the login information (token) on the client. The JWT generated by the server is mainly divided into three parts, a header (algorithm signature), a payload (the login information you need to pass), and signature (header + payload + Signature + server-side key) to the browser and save it. When the user logs in, the client sends the JWT entered by the user to the server, and the server compares the saved and received algorithm signatures with the signature submitted by the user. If consistent, remove the user information from the payload.
(1) Install Microsoft.AspNetCore.Authentication.JwtBearer
//Add configuration class public class JWTsetting { //key value public string? Seckey { get; set; } //Expiration public int ExpireScondes { get; set; } }
//Configure JWT service in program builder.Services.Configure<JWTsetting>(builder.Configuration.GetSection("JWT")); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(opt => { var jwtOpt = builder.Configuration.GetSection("JWT").Get<JWTsetting>(); byte[] keyBytes = Encoding.UTF8.GetBytes(jwtOpt.Seckey); var seckey = new SymmetricSecurityKey(keyBytes); opt.TokenValidationParameters = new() { ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true, ValidateIssuerSigningKey = true, IssuerSigningKey = seckey }; });
//Configure in json configuration class "JWT": { "Seckey":"dandjsndkajndksaj1da51d5s", "ExpireScondes": 3600 }
The second step (and prepare the above Identity framework in advance):
//Add the following in front of app.UseAuthorization();: app.UseAuthentication();
//Add a button to Swagger//to add request headers to other login verification requests. builder.Services.AddSwaggerGen(c => { var scheme = new OpenApiSecurityScheme() { Description = "Authorization header \r\ Example:'Bearer 123456abcdef'", Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Authorization" }, Scheme = "oauth2", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }; c.AddSecurityDefinition("Authorization", scheme); var requirement = new OpenApiSecurityRequirement(); requirement[scheme] = new List<string>(); c.AddSecurityRequirement(requirement); });
//Inject the required classes and interfaces into the controller private readonly UserManager<MyUser> userManager; private readonly RoleManager<MyRole> roleMger; //Determine whether the current environment is a development environment private readonly IWebHostEnvironment webHostEnvironment; //Get key private readonly IOptionsSnapshot<JWTsetting> jwtsnapshot; public DemoController(UserManager<MyUser> userManager, RoleManager<MyRole> roleMger, IWebHostEnvironment webHostEnvironment, IOptionsSnapshot<JWTsetting> jwtsnapshot) { this.userManager = userManager; this.roleMger = roleMger; this.webHostEnvironment = webHostEnvironment; this.jwtsnapshot = jwtsnapshot; }
Add user:
/// <summary> /// Determine whether there are roles and users, and add them /// </summary> /// <returns></returns> [HttpPost] public async Task<ActionResult<string>> Test1(string username,string password,string roleName) { if (await roleMger.RoleExistsAsync("admin") == false) { MyRole myRole = new MyRole { Name = "admin" }; var result = await roleMger.CreateAsync(myRole); if (!result.Succeeded) { return BadRequest("roleMger CreateAsync filed"); } } //Create user using identity MyUser myUser = new MyUser(); myUser.UserName = username; MyUser? user1 = await userManager.FindByNameAsync(username); if (user1 == null) { var result=await userManager.CreateAsync(myUser, password); if (!result.Succeeded) { return BadRequest("User creation failed"); } } //Add a role to the created user var user = await userManager.FindByNameAsync(username); if (user == null) { return BadRequest("User does not exist"); } else { var result = await userManager.AddToRoleAsync(user, roleName); if (result.Succeeded) { return BadRequest("userMger AddToRoleAsync success"); } else { return BadRequest("userMger AddToRoleAsync filed"); } } }
Step 3 (Login Verification):
/// Login verification [HttpPost] public async Task<ActionResult<string>> Login(string username, string password) { var sue = await userManager.FindByNameAsync(username); if (sue == null) { if (webHostEnvironment.IsDevelopment()) { return BadRequest("Username input error"); } else { return BadRequest(); // safer } } //Account locked due to too many errors if (await userManager.IsLockedOutAsync(sue)) { return BadRequest("The user is locked, the lock end time is " + sue.LockoutEnd); } //Detect user and password if (await userManager.CheckPasswordAsync(sue, password)) { await userManager.ResetAccessFailedCountAsync(sue);//If the login is successful, recount the lock //Set jwt to load those things var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.NameIdentifier, sue.Id.ToString())); claims.Add(new Claim(ClaimTypes.Name, sue.UserName)); //Set role access permissions var roles = await userManager.GetRolesAsync(sue); foreach (string role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } //Read the key and expiration time in the configuration string key = jwtsnapshot.Value.Seckey; DateTime expire = DateTime.Now.AddSeconds(jwtsnapshot.Value.ExpireScondes); //Generate JWT to client byte[] secBytes = Encoding.UTF8.GetBytes(key); var seckey = new SymmetricSecurityKey(secBytes); var credentials = new SigningCredentials(seckey, SecurityAlgorithms.HmacSha256Signature); var tokenDescriptor = new JwtSecurityToken(claims: claims, expires: expire, signingCredentials: credentials); string jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor); return jwt; } else { //Record login failure await userManager.AccessFailedAsync(sue); return BadRequest("Username or password is wrong"); } }
The fourth step (if you want to log in using other login methods) is as follows:
[Authorize]//With this feature, this class requires verification when operating. public class Demo2Controller : ControllerBase { [HttpGet] public string Text1() { //Get the value in the message header var claim=this.User.FindFirst(ClaimTypes.Name); return "ok" + claim.Value;//This will get the username of the currently logged in user } [HttpPost] [AllowAnonymous]//Add this and it will not be verified public string Text2() { return "66666"; } [HttpGet] [Authorize(Roles="admin")] public string Text3() { return "3333"; } }
Need to add when logging in and verifying
//The method of adding message header is as follows: Bearer + space + generated jwt Bearer eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA 1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjIiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoibmloYW8 iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1pbiIsImV4cCI6MTY5NTM3MTU1Nn0.SH3ZMTHUQsna18bt2KfRhrgaOmW_ qHSThr8uw8cqkiI
On the jwt generated by the login account, the message header.