Introduction
Majority of web projects needs handling users and therefore user login section. In this article I'm writing about how quickly create user login, logout functionality and display that status.
This article is not related to any specific database so you can use whatever you need, e.g. MSSQL, PostgreSQL, MySQL etc... In this case I'm showing example with MSSQL.
Steps to reach the goal
Step 1. Creating a project.
In those screenshots I'm creating MVC4 project, but this tutorial should work on MVC3 too.
Then select that you are using Razor engine. Check create Tests if you are planning to use it later in your project. If not - leave it unchecked.
Step 2. Creating a database
Right click on App_Data -> Add -> New item... ->Data -> SQL Server Database -> OK.
Now we need a users table.
Right click on Tables and open New Query window.
Now paste code below to that query window and click execute (shortcut CTRL+SHIFT+E)
CREATE TABLE [dbo].[System_Users]
(
[Id] INT NOT NULL IDENTITY ,
[Username] NVARCHAR(50) NOT NULL,
[Password] NVARCHAR(MAX) NOT NULL,
[RegDate] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
[Email] NVARCHAR(50) NOT NULL,
PRIMARY KEY ([Id])
)
GO
CREATE INDEX [IX_System_Users_Username] ON [dbo].[System_Users] ([Username])
GO
INSERT INTO [dbo].[System_Users]
([Username], [Password], [Email])
VALUES
('test', 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3', 'test@test.test')
GO
This code has created a table and inserted user test with password test. Password is encoded with SHA1. To generate your own - google online converter to sha1, but in this example better leave it as it is.
Step 3. Creating a HomeController
OK. Now we need a home controller which will be our first page.
Step 4. Creating a Home view.
Right click on method name -> Create view.
Call it Index (The same as method name) and select to use layout.
Step 5. Creating a User model
User model is required to handle user information and for form creation.
Right click on Models -> Add -> New item... -> Code -> Class; Name it User.cs.
In User class code should look like this:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
namespace Creating_a_custom_user_login_form.Models
{
public class User
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember on this computer")]
public bool RememberMe { get; set; }
public bool IsValid(string _username, string _password)
{
using (var cn = new SqlConnection(@"Data Source=(LocalDB)\v11.0;AttachDbFilename" +
@"='C:\Tutorials\1 - Creating a custom user login form\Creating " +
@"a custom user login form\App_Data\Database1.mdf';Integrated Security=True"))
{
string _sql = @"SELECT [Username] FROM [dbo].[System_Users] " +
@"WHERE [Username] = @u AND [Password] = @p";
var cmd = new SqlCommand(_sql, cn);
cmd.Parameters
.Add(new SqlParameter("@u", SqlDbType.NVarChar))
.Value = _username;
cmd.Parameters
.Add(new SqlParameter("@p", SqlDbType.NVarChar))
.Value = Helpers.SHA1.Encode(_password);
cn.Open();
var reader = cmd.ExecuteReader();
if (reader.HasRows)
{
reader.Dispose();
cmd.Dispose();
return true;
}
else
{
reader.Dispose();
cmd.Dispose();
return false;
}
}
}
}
}
It could be necessary to modify a connection string on your computer.
var cn = new SqlConnection(@"Data Source=(LocalDB)\v11.0;AttachDbFilename='C:\Tutorials\1 - Creating a custom user login form\Creating a custom user login form\App_Data\Database1.mdf';Integrated Security=True")
It can be found here:
NOTE: connection string must be placed in web.config!
Step 6. Creating additional helpers
As you may noted Helpers.SHA1.Encode(_password);
is underlined in red. It's because there's no such class and method yet.
Now we are going to add additional shared project to our solution and create our Helper.
In solution Explorer right click on Solution then Add -> New Project... -> Windows -> Class Library; Name it Helpers.
In Solution Explorer right click on Helpers project and Add -> New item... -> Code -> Class; Name it SHA1.
Code int his class must should look like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Helpers
{
public class SHA1
{
public static string Encode(string value)
{
var hash = System.Security.Cryptography.SHA1.Create();
var encoder = new System.Text.ASCIIEncoding();
var combined = encoder.GetBytes(value ?? "");
return BitConverter.ToString(hash.ComputeHash(combined)).ToLower().Replace("-", "");
}
}
}
Now we need to reference it to our main project. Right click on our website project then Add Reference... -> Select Helpers (checkbox) and hit OK.
Step 7. Creating User Controller
We need a user controller to manage user who's about to log in or log out. Create controller as you did previous and name it UserController. I preffer naming it User (not Users) becouse it stands for ONE user.
This is code which should appear in it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace Creating_a_custom_user_login_form.Controllers
{
public class UserController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(Models.User user)
{
if (ModelState.IsValid)
{
if (user.IsValid(user.UserName, user.Password))
{
FormsAuthentication.SetAuthCookie(user.UserName, user.RememberMe);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Login data is incorrect!");
}
}
return View(user);
}
public ActionResult Logout()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
}
}
Step 8. Creating a login view
Right click on Login method name and create view.
Use layout template as previously.
Step 9. Making login form
Code should look like this:
@model Creating_a_custom_user_login_form.Models.User
@{
ViewBag.Title = "Login";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
@Html.ValidationSummary(true, "Login failed. Check your login details.");
<div>
<fieldset>
<legend>Login</legend>
<div class="editor-label">
@Html.LabelFor(u => u.UserName)
</div>
<div class="editor-field">
@Html.TextBoxFor(u => u.UserName)
@Html.ValidationMessageFor(u => u.UserName)
</div>
<div class="editor-label">
@Html.LabelFor(u => u.Password)
</div>
<div class="editor-field">
@Html.PasswordFor(u => u.Password)
@Html.ValidationMessageFor(u => u.Password)
</div>
<div class="editor-label">
@Html.CheckBoxFor(u => u.RememberMe)
@Html.LabelFor(u => u.RememberMe)
</div>
<input type="submit" value="Log In" />
</fieldset>
</div>
}
Here we create our form, add labels and validators.
Step 10. Editing _Layout.cshtml page (add login button)
This file is in Views -> Shared folder.
We are going to add this code so it will allow us to login, log out and displays our name.
<div style="width: auto; background-color: #728ea7;">
@if (Request.IsAuthenticated) {
<strong>@Html.Encode(User.Identity.Name)</strong>
@Html.ActionLink("Sign Out", "Logout", "User")
}
else {
@Html.ActionLink("Register", "Register", "User")
<span> | </span>
@Html.ActionLink("Sign In", "Login", "User")
}
</div>
It checks (Request.IsAuthenticated
) if user is logged in and then displays its name. Also it displays Register button, but in this article I'm not going further with it.
The whole _Layout.cshtml code should look something like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div style="width: auto; background-color: #728ea7;">
@if (Request.IsAuthenticated) {
<strong>@Html.Encode(User.Identity.Name)</strong>
@Html.ActionLink("Sign Out", "Logout", "User")
}
else {
@Html.ActionLink("Register", "Register", "User")
<span> | </span>
@Html.ActionLink("Sign In", "Login", "User")
}
</div>
@RenderBody()
@Scripts.Render("~/bundles/jquery")
@RenderSection("scripts", required: false)
</body>
</html>
Thats it!
The source code can be downloaded here.
Review
So what we have did here. We created a table with test user. Data is hashed, so we can call it kind of secure. We have included external project with helper functions. It can be filled with more useful stuff and shared with other your projects in the same way you did here. (I like to seperate Interfaces and Business logic like this too).
Next we build two controllers and views. One for first page and other for user login.
We have edited our Layout so we could see some information and login buttons all the time (while we are using that layout)
I hope this article helped you to understand basic usage and will help you in future.