ASP.NET Core Razor Pages: Simple Login using Entity Framework Database First Approach
- Service Oriented Architecture as .NET Microservices.
- Dependency Injection design pattern.
In today's tutorial I will demonstrate the creation of a razor pages base simple asp.net core login application using entity framework database first approach.
Prerequisites:
Following are some prerequisites before you proceed any further in this tutorial:- Basic understanding of ASP.NET Core framework.
- Upgrade Windows Power Shell to latest version.
- Knowledge about entity framework
- Knowledge about Claim Base Identity Model
- Knowledge about Bootstrap.
- Knowledge about C# programming.
- Knowledge about C# LINQ.
Download Now!
Let's begin now.1) First create your existing SQL server database named "db_corelogin" which will be utilized in asp.net core web application by executing following SQL script i.e.
USE [db_corelogin] GO /****** Object: StoredProcedure [dbo].[LoginByUsernamePassword] Script Date: 9/5/2018 8:39:02 PM ******/ DROP PROCEDURE [dbo].[LoginByUsernamePassword] GO /****** Object: Table [dbo].[Login] Script Date: 9/5/2018 8:39:02 PM ******/ DROP TABLE [dbo].[Login] GO /****** Object: Table [dbo].[Login] Script Date: 9/5/2018 8:39:02 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[Login]( [id] [int] IDENTITY(1,1) NOT NULL, [username] [varchar](50) NOT NULL, [password] [varchar](50) NOT NULL, CONSTRAINT [PK_Login] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO SET IDENTITY_INSERT [dbo].[Login] ON INSERT [dbo].[Login] ([id], [username], [password]) VALUES (1, N'my-login', N'my-password-123') SET IDENTITY_INSERT [dbo].[Login] OFF /****** Object: StoredProcedure [dbo].[LoginByUsernamePassword] Script Date: 9/5/2018 8:39:02 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: <Author,,Asma Khalid> -- Create date: <Create Date,,15-Mar-2016> -- Description: <Description,,You are Allow to Distribute this Code> -- ============================================= CREATE PROCEDURE [dbo].[LoginByUsernamePassword] @username varchar(50), @password varchar(50) AS BEGIN SELECT id, username, password FROM Login WHERE username = @username AND password = @password END GO
In the above script, I have created a login table with existing user login data and also I have created a store procedure to verify the existing login information.
2) Now, create a new .Net core web application project and name it "CoreLoginEfDbFirst" as shown below i.e.
3) Build the solution and ensure that the build is successful then restart visual studio.
4) Now, in order to import existing database context object using entity framework to my core web application. I need to install following library packages via "Tools->NuGet Package Manager->Manage NuGet Packages for Solution" in below mention order i.e.
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
- Microsoft.EntityFrameworkCore.SqlServer.Design
5) Click "Tools->NuGet Package Manager->Package Manager Console" as shown below i.e.
6) Type the following command inside the console as shown below. Do not forget to update your SQL server connection string configuration in this command i.e.
Scaffold-DbContext "Server=SQL SERVER (e.g localhost);Database=DATABASE (e.g db_corelogin);Trusted_Connection=True;user id=SQL USERNAME;password=SQL PASSWORD;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models/DB
The above command will create following folders and files i.e.
Remember that when we use database first approach in asp.net mvc framework, we have a cool graphical designer UI through which we select the tables, store procedures and other database objects to be imported into the web application via entity framework. The database context file also get imported through which we communicate to SQL database engine. In .net core however, there is no cool graphical user interface to import the SQL database context, we have to import the database context via above command and then inside the created .cs database context file, we need to write appropriate business logic methods to access SQL database tables, store procedures or queries to access the data. Login.cs class is the object class of our SQL database table Login.
7) Now, create "Models\DB\LoginByUsernamePassword.cs" file and replace the following code in this file i.e.
using System; using System.Collections.Generic; namespace CoreLoginEfDbFirst.Models.DB { public partial class LoginByUsernamePassword { public int Id { get; set; } public string Username { get; set; } public string Password { get; set; } } }
In the above code, I have created an object class for my store procedure returning data.
8) Now, create "Models\LoginViewModel.cs" file and replace the following code in it i.e.
//----------------------------------------------------------------------- // <copyright file="LoginViewModel.cs" company="None"> // Copyright (c) Allow to distribute this code and utilize this code for personal or commercial purpose. // </copyright> // <author>Asma Khalid</author> //----------------------------------------------------------------------- namespace CoreLoginEfDbFirst.Models { using System.Collections.Generic; using System.ComponentModel.DataAnnotations; /// <summary> /// Login view model class. /// </summary> public class LoginViewModel { #region Properties /// <summary> /// Gets or sets to username address. /// </summary> [Required] [Display(Name = "Username")] public string Username { get; set; } /// <summary> /// Gets or sets to password address. /// </summary> [Required] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } #endregion } }
The above piece of code is my view model class which will be attached to the target view in order to process user inputs.
9) Now, open "Models\DB\db_coreloginContext.cs"file and replace the following code in it i.e.
using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; namespace CoreLoginEfDbFirst.Models.DB { public partial class db_coreloginContext : DbContext { public db_coreloginContext() { } public db_coreloginContext(DbContextOptions<db_coreloginContext> options) : base(options) { } public virtual DbSet<Login> Login { get; set; } ////protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) ////{ //// if (!optionsBuilder.IsConfigured) //// { //// optionsBuilder.UseSqlServer("Server=SQL SERVER;Database=DATABASE;User id=SQL USERNAME;Password=SQL PASSWORD;Trusted_Connection=True;"); //// } ////} protected override void OnModelCreating(ModelBuilder modelBuilder) { ////modelBuilder.Entity<Login>(entity => ////{ //// entity.Property(e => e.Id).HasColumnName("id"); //// entity.Property(e => e.Password) //// .IsRequired() //// .HasColumnName("password") //// .HasMaxLength(50) //// .IsUnicode(false); //// entity.Property(e => e.Username) //// .IsRequired() //// .HasColumnName("username") //// .HasMaxLength(50) //// .IsUnicode(false); ////}); // [Asma Khalid]: Query for store procedure. modelBuilder.Query<LoginByUsernamePassword>(); } #region Login by username and password store procedure method. /// <summary> /// Login by username and password store procedure method. /// </summary> /// <param name="usernameVal">Username value parameter</param> /// <param name="passwordVal">Password value parameter</param> /// <returns>Returns - List of logins by username and password</returns> public async Task<List<LoginByUsernamePassword>> LoginByUsernamePasswordMethodAsync(string usernameVal, string passwordVal) { // Initialization. List<LoginByUsernamePassword> lst = new List<LoginByUsernamePassword>(); try { // Settings. SqlParameter usernameParam = new SqlParameter("@username", usernameVal ?? (object)DBNull.Value); SqlParameter passwordParam = new SqlParameter("@password", passwordVal ?? (object)DBNull.Value); // Processing. string sqlQuery = "EXEC [dbo].[LoginByUsernamePassword] " + "@username, @password"; lst = await this.Query<LoginByUsernamePassword>().FromSql(sqlQuery, usernameParam, passwordParam).ToListAsync(); } catch (Exception ex) { throw ex; } // Info. return lst; } #endregion } }
As I have already mentioned that .net core heavily utilizes "Service Oriented Architecture as .NET Microservices and Dependency Injection design pattern" concepts. Therefore, we need to make some changes in the above auto generated database context class, otherwise we will not be able to communicate with SQL server database engine. So, in the above class ensure that "OnConfiguring(...)" is either removed or commented out as I have already commented out this method in "db_coreloginContext.cs" file. Ensure that your database context file above must also have overload constructor as shown below i.e.
public db_coreloginContext(DbContextOptions<db_coreloginContext> options) : base(options) { }
Rest is your choice i.e what new database logic methods you need to add or remove any existing entities. In order to access data objects from SQL database via custom queries, store procedures or direct query from tables. You need to register your target custom objects with the model builder inside your "OnModelCreating(...)" method of "db_coreloginContext.cs" database context class. As shown below that I will not do any direct table query so, I have commented out my table entity object Login pre-build registration with the model builder and since, I am using the result of my SQL server database store procedure, therefore, I have registered my custom store procedure data returining object with the model builder i.e.
protected override void OnModelCreating(ModelBuilder modelBuilder) { ////modelBuilder.Entity<Login>(entity => ////{ //// entity.Property(e => e.Id).HasColumnName("id"); //// entity.Property(e => e.Password) //// .IsRequired() //// .HasColumnName("password") //// .HasMaxLength(50) //// .IsUnicode(false); //// entity.Property(e => e.Username) //// .IsRequired() //// .HasColumnName("username") //// .HasMaxLength(50) //// .IsUnicode(false); ////}); // [Asma Khalid]: Query for store procedure. modelBuilder.Query<LoginByUsernamePassword>(); }
Then, I have created "LoginByUsernamePasswordMethodAsync(...)" method, which is access to my store procedure inside SQL server database i.e.
#region Login by username and password store procedure method. /// <summary> /// Login by username and password store procedure method. /// </summary> /// <param name="usernameVal">Username value parameter</param> /// <param name="passwordVal">Password value parameter</param> /// <returns>Returns - List of logins by username and password</returns> public async Task<List<LoginByUsernamePassword>> LoginByUsernamePasswordMethodAsync(string usernameVal, string passwordVal) { // Initialization. List<LoginByUsernamePassword> lst = new List<LoginByUsernamePassword>(); try { // Settings. SqlParameter usernameParam = new SqlParameter("@username", usernameVal ?? (object)DBNull.Value); SqlParameter passwordParam = new SqlParameter("@password", passwordVal ?? (object)DBNull.Value); // Processing. string sqlQuery = "EXEC [dbo].[LoginByUsernamePassword] " + "@username, @password"; lst = await this.Query<LoginByUsernamePassword>().FromSql(sqlQuery, usernameParam, passwordParam).ToListAsync(); } catch (Exception ex) { throw ex; } // Info. return lst; } #endregion
10) In order to access SQL server database I need to store my database connection string in "appsettings.json" file which is the recommended way. So, open "appsettings.json" file and replace the following code in it i.e.
{ "ConnectionStrings": { "db_corelogin": "Server=SQL SERVER;Database=DATABASE;Trusted_Connection=True;user id=SQL USERNAME;password=SQL PASSWORD;" }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Warning" } } }
Do not forget to update your SQL server configurations in the above connection string.
11) Now, I need to register my database context as .NET Microservices with the .net core framework in order to access my database within my application. To do so, open the "Startup.cs" file and add following line of code at the end of "ConfigureServices(...)" method i.e.
// [Asma Khalid]: Register SQL database configuration context as services. services.AddDbContext<db_coreloginContext>(options => options.UseSqlServer(Configuration.GetConnectionString("db_corelogin")));
12) Do a little cleanup of your folder hierarchy i.e.
In the above code, I have created a basic layout view of my web application.
14) Now, create "Pages\Index.cshtml" file with model and replace the following code in it i.e.
In the above code, I have create a simple login form for the razor page with the combination of razor auto generated code as similar to asp.net mvc framework and asp- attributes within the HTML tags as prescribe for razor pages.
15) Open, "Pages\Index.cshtml.cs" file and replace following code in it i.e.
Let's diagnose the above code chunk by chunk i.e.
In asp.net core framework unlike asp.net mvc framework, in order to bind the view model class to the UI I need to define "[BindProperty]" data annotation above the view model public property which is defined within the IndexModel class "Pages\Index.cshtml.cs" file. as shown below i.e.
Now, below "OnGet()" method is the default mandatory method which will be executed when the page is called. I have simple verified the user authorization in the "OnGet()" method i.e. if user is login then go to home page otherwise go to login page i.e.
Next, I have created the "OnPostLogIn(..)" method which will verify the username and password via SQL database access and then by using claim base identity model mechanism sign in the user into my web application. Here, notice that in order to tell the framework that the method is post, I need to mandatory write the prefix "OnPost" before writing the handler name of my choice i.e. LogIn . So, it will become "OnPostLogin()" method, this naming convention is important. A helper "SignInUser(...)" method is also written for signing in the user via claim base identity model mechanism i.e.
To access the "OnPostLogIn(...)" method in the HTML, you do not need to write the "OnPost" prefix, but, only "LogIn" as shown in the below line of code from "Pages\Index.cshtml" file which will post request to the "OnPostLogIn(...)" method i.e.
16) Open, "Startup.cs" file to register the authorization services with the .net core framework in order to utilize the authentication services of the claim base identity model mechanism. Go to, "ConfigureServices(...)" method and add following lines of code before the database context registration i.e.
17) In "Startup.cs" file for authenticaon purpose add following lines of code at the end of "Configure(...)" method i.e.
The final "Startup.cs" file will look as shown below i.e.
18) Create "Pages\Home\Index.cshtml" file and replace the following code in it i.e.
In the above code, I have created a simple home page which is accessible to only authorize users of this application.
19) Now, open "Pages\Home\Index.cshtml.cs" file and replace the following in it i.e.
In the above code, I have simply crated a default "OnGet()" method and "OnPostLogOff(...)" method. I am also using "[Authorize]" data annotation on this class in order to prevent unauthorize access to this application.
20) Execute the project and you will be able to see the following i.e.
- Remove "Pages\_Layout.cshtml" file.
- Move "Pages\_ValidationScriptsPartial.cshtml" file to Views\Shared folder.
- Except "Pages\_ViewImports.cshtml" file, "Pages\_ViewStart.cshtml" file and "Pages\Error.cshtml" file, remove all other files inside "Pages" folder.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>@ViewBag.Title</title> <environment include="Development"> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /> <link rel="stylesheet" href="~/css/site.css" /> </environment> <environment exclude="Development"> <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> </environment> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div class="navbar-collapse collapse"> </div> </div> </nav> <div class="container body-content"> @RenderBody() <hr /> <footer> <center> <p><strong>Copyright © @DateTime.Now.Year - <a href="http://wwww.asmak9.com/">Asma's Blog</a>.</strong> All rights reserved.</p> </center> </footer> </div> <environment include="Development"> <script src="~/lib/jquery/dist/jquery.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> <script src="~/js/site.js" asp-append-version="true"></script> </environment> <environment exclude="Development"> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js" asp-fallback-src="~/lib/jquery/dist/jquery.min.js" asp-fallback-test="window.jQuery" crossorigin="anonymous" integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk"> </script> <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" crossorigin="anonymous" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"> </script> <script src="~/js/site.min.js" asp-append-version="true"></script> </environment> @RenderSection("Scripts", required: false) </body> </html>
In the above code, I have created a basic layout view of my web application.
14) Now, create "Pages\Index.cshtml" file with model and replace the following code in it i.e.
@page "{handler?}" @model IndexModel @{ ViewBag.Title = "ASP.NET Core Razor Pages - Simple Login Database First Approach"; } <h2>@ViewBag.Title.</h2> <div class="row"> <div class="col-md-8"> <section> <form method="post" role="form" class="form-horizontal"> @Html.AntiForgeryToken() <h4>Use a local account to log in.</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(m => m.LoginModel.Username, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.LoginModel.Username, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.LoginModel.Username, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.LoginModel.Password, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.LoginModel.Password, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.LoginModel.Password, "", new { @class = "text-danger" }) </div> </div> @*<div class="form-group"> <label asp-for="LoginModel.Username" class = "col-md-2 control-label"></label> <div class="col-md-10"> <input asp-for="LoginModel.Username" class="form-control" /> <span class="alert-danger" asp-validation-for="LoginModel.Username"></span> </div> </div> <div class="form-group"> <label asp-for="LoginModel.Password" class="col-md-2 control-label"></label> <div class="col-md-10"> <input asp-for="LoginModel.Password" class="form-control" /> <span class="alert-danger" asp-validation-for="LoginModel.Password"></span> </div> </div>*@ <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <button asp-page-handler="LogIn" class="btn btn-default">Log in</button> </div> </div> </form> </section> </div> </div>
In the above code, I have create a simple login form for the razor page with the combination of razor auto generated code as similar to asp.net mvc framework and asp- attributes within the HTML tags as prescribe for razor pages.
15) Open, "Pages\Index.cshtml.cs" file and replace following code in it i.e.
//----------------------------------------------------------------------- // <copyright file="Index.cshtml.cs" company="None"> // Copyright (c) Allow to distribute this code and utilize this code for personal or commercial purpose. // </copyright> // <author>Asma Khalid</author> //----------------------------------------------------------------------- namespace CoreLoginEfDbFirst.Pages { using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using CoreLoginEfDbFirst.Models; using CoreLoginEfDbFirst.Models.DB; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; /// <summary> /// Index page model class. /// </summary> public class IndexModel : PageModel { #region Private Properties. /// <summary> /// Database Manager property. /// </summary> private readonly db_coreloginContext databaseManager; #endregion #region Default Constructor method. /// <summary> /// Initializes a new instance of the <see cref="IndexModel"/> class. /// </summary> /// <param name="databaseManagerContext">Database manager context parameter</param> public IndexModel(db_coreloginContext databaseManagerContext) { try { // Settings. this.databaseManager = databaseManagerContext; } catch (Exception ex) { // Info Console.Write(ex); } } #endregion #region Public Properties /// <summary> /// Gets or sets login model property. /// </summary> [BindProperty] public LoginViewModel LoginModel { get; set; } #endregion #region On Get method. /// <summary> /// GET: /Index /// </summary> /// <returns>Returns - Appropriate page </returns> public IActionResult OnGet() { try { // Verification. if (this.User.Identity.IsAuthenticated) { // Home Page. return this.RedirectToPage("/Home/Index"); } } catch (Exception ex) { // Info Console.Write(ex); } // Info. return this.Page(); } #endregion #region On Post Login method. /// <summary> /// POST: /Index/LogIn /// </summary> /// <returns>Returns - Appropriate page </returns> public async Task<IActionResult> OnPostLogIn() { try { // Verification. if (ModelState.IsValid) { // Initialization. var loginInfo = await this.databaseManager.LoginByUsernamePasswordMethodAsync(this.LoginModel.Username, this.LoginModel.Password); // Verification. if (loginInfo != null && loginInfo.Count() > 0) { // Initialization. var logindetails = loginInfo.First(); // Login In. await this.SignInUser(logindetails.Username, false); // Info. return this.RedirectToPage("/Home/Index"); } else { // Setting. ModelState.AddModelError(string.Empty, "Invalid username or password."); } } } catch (Exception ex) { // Info Console.Write(ex); } // Info. return this.Page(); } #endregion #region Helpers #region Sign In method. /// <summary> /// Sign In User method. /// </summary> /// <param name="username">Username parameter.</param> /// <param name="isPersistent">Is persistent parameter.</param> /// <returns>Returns - await task</returns> private async Task SignInUser(string username, bool isPersistent) { // Initialization. var claims = new List<Claim>(); try { // Setting claims.Add(new Claim(ClaimTypes.Name, username)); var claimIdenties = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var claimPrincipal = new ClaimsPrincipal(claimIdenties); var authenticationManager = Request.HttpContext; // Sign In. await authenticationManager.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimPrincipal, new AuthenticationProperties() { IsPersistent = isPersistent }); } catch (Exception ex) { // Info throw ex; } } #endregion #endregion } }
Let's diagnose the above code chunk by chunk i.e.
#region Private Properties. /// <summary> /// Database Manager property. /// </summary> private readonly db_coreloginContext databaseManager;
The above piece of code is basically a read only property to store the reference of my database context object as a part or dependency injection design pattern. Then in the below code I have created an overload constructor with the database context object as a parameter following dependency injection design pattern and within my overload constructor I have stored the reference of the database context object for my class i.e.
#region Default Constructor method. /// <summary> /// Initializes a new instance of the <see cref="IndexModel"/> class. /// </summary> /// <param name="databaseManagerContext">Database manager context parameter</param> public IndexModel(db_coreloginContext databaseManagerContext) { try { // Settings. this.databaseManager = databaseManagerContext; } catch (Exception ex) { // Info Console.Write(ex); } } #endregion
In asp.net core framework unlike asp.net mvc framework, in order to bind the view model class to the UI I need to define "[BindProperty]" data annotation above the view model public property which is defined within the IndexModel class "Pages\Index.cshtml.cs" file. as shown below i.e.
#region Public Properties /// <summary> /// Gets or sets login model property. /// </summary> [BindProperty] public LoginViewModel LoginModel { get; set; } #endregion
Now, below "OnGet()" method is the default mandatory method which will be executed when the page is called. I have simple verified the user authorization in the "OnGet()" method i.e. if user is login then go to home page otherwise go to login page i.e.
#region On Get method. /// <summary> /// GET: /Index /// </summary> /// <returns>Returns - Appropriate page </returns> public IActionResult OnGet() { try { // Verification. if (this.User.Identity.IsAuthenticated) { // Home Page. return this.RedirectToPage("/Home/Index"); } } catch (Exception ex) { // Info Console.Write(ex); } // Info. return this.Page(); } #endregion
Next, I have created the "OnPostLogIn(..)" method which will verify the username and password via SQL database access and then by using claim base identity model mechanism sign in the user into my web application. Here, notice that in order to tell the framework that the method is post, I need to mandatory write the prefix "OnPost" before writing the handler name of my choice i.e. LogIn . So, it will become "OnPostLogin()" method, this naming convention is important. A helper "SignInUser(...)" method is also written for signing in the user via claim base identity model mechanism i.e.
#region On Post Login method. /// <summary> /// POST: /Index/LogIn /// </summary> /// <returns>Returns - Appropriate page </returns> public async Task<IActionResult> OnPostLogIn() { try { // Verification. if (ModelState.IsValid) { // Initialization. var loginInfo = await this.databaseManager.LoginByUsernamePasswordMethodAsync(this.LoginModel.Username, this.LoginModel.Password); // Verification. if (loginInfo != null && loginInfo.Count() > 0) { // Initialization. var logindetails = loginInfo.First(); // Login In. await this.SignInUser(logindetails.Username, false); // Info. return this.RedirectToPage("/Home/Index"); } else { // Setting. ModelState.AddModelError(string.Empty, "Invalid username or password."); } } } catch (Exception ex) { // Info Console.Write(ex); } // Info. return this.Page(); } #endregion #region Helpers #region Sign In method. /// <summary> /// Sign In User method. /// </summary> /// <param name="username">Username parameter.</param> /// <param name="isPersistent">Is persistent parameter.</param> /// <returns>Returns - await task</returns> private async Task SignInUser(string username, bool isPersistent) { // Initialization. var claims = new List<Claim>(); try { // Setting claims.Add(new Claim(ClaimTypes.Name, username)); var claimIdenties = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var claimPrincipal = new ClaimsPrincipal(claimIdenties); var authenticationManager = Request.HttpContext; // Sign In. await authenticationManager.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimPrincipal, new AuthenticationProperties() { IsPersistent = isPersistent }); } catch (Exception ex) { // Info throw ex; } } #endregion #endregion
To access the "OnPostLogIn(...)" method in the HTML, you do not need to write the "OnPost" prefix, but, only "LogIn" as shown in the below line of code from "Pages\Index.cshtml" file which will post request to the "OnPostLogIn(...)" method i.e.
<div class="form-group"> <div class="col-md-offset-2 col-md-10"> <button asp-page-handler="LogIn" class="btn btn-default">Log in</button> </div> </div>
16) Open, "Startup.cs" file to register the authorization services with the .net core framework in order to utilize the authentication services of the claim base identity model mechanism. Go to, "ConfigureServices(...)" method and add following lines of code before the database context registration i.e.
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // [Asma Khalid]: Authorization settings. services.AddAuthentication(options => { options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }).AddCookie(options => { options.LoginPath = new PathString("/Index"); options.ExpireTimeSpan = TimeSpan.FromMinutes(5.0); }); // [Asma Khalid]: Authorization settings. services.AddMvc().AddRazorPagesOptions(options => { options.Conventions.AuthorizeFolder("/"); options.Conventions.AllowAnonymousToPage("/Index"); }); // [Asma Khalid]: Register SQL database configuration context as services. services.AddDbContext<db_coreloginContext>(options => options.UseSqlServer(Configuration.GetConnectionString("db_corelogin"))); }
17) In "Startup.cs" file for authenticaon purpose add following lines of code at the end of "Configure(...)" method i.e.
// [Asma Khalid]: Register simple authorization.
app.UseAuthentication();
The final "Startup.cs" file will look as shown below i.e.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CoreLoginEfDbFirst.Models.DB; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace CoreLoginEfDbFirst { 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) { // [Asma Khalid]: Authorization settings. services.AddAuthentication(options => { options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }).AddCookie(options => { options.LoginPath = new PathString("/Index"); options.ExpireTimeSpan = TimeSpan.FromMinutes(5.0); }); // [Asma Khalid]: Authorization settings. services.AddMvc().AddRazorPagesOptions(options => { options.Conventions.AuthorizeFolder("/"); options.Conventions.AllowAnonymousToPage("/Index"); }); // [Asma Khalid]: Register SQL database configuration context as services. services.AddDbContext<db_coreloginContext>(options => options.UseSqlServer(Configuration.GetConnectionString("db_corelogin"))); } // 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.UseBrowserLink(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } app.UseStaticFiles(); // [Asma Khalid]: Register simple authorization. app.UseAuthentication(); app.UseMvc(); } } }
18) Create "Pages\Home\Index.cshtml" file and replace the following code in it i.e.
@page @model CoreLoginEfDbFirst.Pages.Home.IndexModel @{ ViewBag.Title = "ASP.NET Core Razor Pages - Simple Login Database First Approach"; } <h2>@ViewBag.Title.</h2> <div class="jumbotron"> <h1>Welcome</h1> <p class="lead">Login from "@User.Identity.Name" Account.</p> </div> <div class="row"> <div class="col-md-8"> <form method="post" role="form" class="form-horizontal"> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <button asp-page-handler="LogOff" class="btn btn-default">Log Off</button> </div> </div> </form> </div> </div>
In the above code, I have created a simple home page which is accessible to only authorize users of this application.
19) Now, open "Pages\Home\Index.cshtml.cs" file and replace the following in it i.e.
//----------------------------------------------------------------------- // <copyright file="Index.cshtml.cs" company="None"> // Copyright (c) Allow to distribute this code and utilize this code for personal or commercial purpose. // </copyright> // <author>Asma Khalid</author> //----------------------------------------------------------------------- namespace CoreLoginEfDbFirst.Pages.Home { using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; /// <summary> /// Index page model class. /// </summary> [Authorize] public class IndexModel : PageModel { #region On Get method. /// <summary> /// On Get method. /// </summary> public void OnGet() { try { } catch (Exception ex) { // Info Console.Write(ex); } } #endregion #region Log Out method. /// <summary> /// POST: /Home/Index/LogOff /// </summary> /// <returns>Return log off action</returns> public async Task<IActionResult> OnPostLogOff() { try { // Setting. var authenticationManager = Request.HttpContext; // Sign Out. await authenticationManager.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); } catch (Exception ex) { // Info throw ex; } // Info. return this.RedirectToPage("/Index"); } #endregion } }
In the above code, I have simply crated a default "OnGet()" method and "OnPostLogOff(...)" method. I am also using "[Authorize]" data annotation on this class in order to prevent unauthorize access to this application.
20) Execute the project and you will be able to see the following i.e.
As you have now understood the usage of ‘Record and Playback’ tool, the following are the different posts using which you can explore the functioning of ‘Selenium IDE’
ReplyDeleteselenium Training in chennai
Thanks! very nice article and well explanation!
ReplyDeleteEach rider must know the rules and regulations applicable in driving a electric scooter seat in their respective areas.
ReplyDeleteThe website is looking bit flashy and it catches the visitors eyes. Design is pretty simple and a good user friendly interface. Famous People net worth
ReplyDeleteA debt of gratitude is in order for sharing the information, keep doing awesome... I truly delighted in investigating your site. great asset... shaving razor
ReplyDeleteI was surfing the Internet for information and came across your blog. I am impressed by the information you have on this blog. It shows how well you understand this subject. https://sites.google.com/site/hotmailloginonline/
ReplyDeleteGreat job for publishing such a beneficial web site. Your web log isn’t only useful but it is additionally really creative too. https://sites.google.com/site/hotmailloginonline/
ReplyDeleteThis is my first visit to your web journal! We are a group of volunteers and new activities in the same specialty. Website gave us helpful data to work. entrar hotmail
ReplyDelete