1. add Authorize annotation to controller
for example, in AppController:
[Authorize]
public IActionResult Shop() {
var results = this.repository.GetAllProducts();
return View(results);
}
2. Create a new entity in Data/Entities folder called StoreUser.cs
public class StoreUser:IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
3. in DbContext file, for example, in DutchContext.cs file, make changes below:
public class DutchContext:IdentityDbContext<StoreUser>
{
public DutchContext(DbContextOptions<DutchContext> dbContextOptions):base(dbContextOptions)
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}
4. in Order.cs entity file, add a new property of User
public class Order
{
public int Id { get; set; }
public DateTime OrderDate { get; set; }
public string OrderNumber { get; set; }
public ICollection<OrderItem> Items { get; set; }
public StoreUser User { get; set; }
}
5. in command line, run
dotnet ef migrations add Identity
dotnet ef database drop
6. make changes in seed file
for example, in DutchSeeder.cs
public class DutchSeeder
{
private readonly DutchContext context;
private readonly IHostingEnvironment hosting;
private readonly UserManager<StoreUser> userManager;
public DutchSeeder(DutchContext context,
IHostingEnvironment hosting,
UserManager<StoreUser> userManager) {
this.context = context;
this.hosting = hosting;
this.userManager = userManager;
}
public async Task Seed()
{
this.context.Database.EnsureCreated();
var user = await this.userManager.FindByEmailAsync("a@b.com");
if (user == null) {
user = new StoreUser()
{
FirstName = "a",
LastName = "b",
UserName = "a@b.com",
Email = "a@b.com"
};
var result = await this.userManager.CreateAsync(user, "mypassword");
if (result != IdentityResult.Success) {
throw new InvalidOperationException("Fail to create default user");
}
}
if (!this.context.Products.Any()) {
var filePath = Path.Combine(this.hosting.ContentRootPath, "Data/art.json");
var json = File.ReadAllText(filePath);
var products = JsonConvert.DeserializeObject<IEnumerable<Product>>(json);
this.context.AddRange(products);
var order = new Order()
{
OrderDate = DateTime.Now,
OrderNumber = "12345",
User = user,
Items = new List<OrderItem>() {
new OrderItem(){
Product = products.First(),
Quantity = 5,
UnitPrice = products.First().Price
}
}
};
this.context.Add(order);
this.context.SaveChanges();
}
}
}
7. in Startup.cs,
in ConfigureServices method, add
services.AddIdentity<StoreUser, IdentityRole>(cfg =>
{
cfg.User.RequireUniqueEmail = true;
}).AddEntityFrameworkStores<DutchContext>();
in Configure, add
app.UseAuthentication(); before app.UseMvc
and change seeder.Seed()
to seeder.Seed().Wait();
8. create AccountController, LoginViewModel and Login page
***AccountController
public class AccountController : Controller
{
private readonly ILogger<AccountController> logger;
private readonly SignInManager<StoreUser> signInManager;
public AccountController(ILogger<AccountController> logger,
SignInManager<StoreUser> signInManager)
{
this.logger = logger;
this.signInManager = signInManager;
}
public IActionResult Login()
{
if (this.User.Identity.IsAuthenticated) {
return RedirectToAction("Index", "App");
}
return View();
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid) {
var result = await this.signInManager.PasswordSignInAsync(model.Username,
model.Password,
model.RememberMe,
false);
if (result.Succeeded) {
if (Request.Query.Keys.Contains("ReturnUrl")) {
return Redirect(Request.Query["ReturnUrl"].First());
}
else
{
return RedirectToAction("Shop", "App");
}
}
}
ModelState.AddModelError("", "Failed to login");
return View();
}
}
***Login page
@model LoginViewModel
@{
ViewBag.Title = "Login";
}
@section scripts{
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js"></script>
}
<div class="row">
<div class="col-md-4 col-md-offset-4">
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<div class="form-group">
<label asp-for="Username">UserName</label>
<input asp-for="Username" class="form-control"/>
<span asp-validation-for="Username" class="text-warning"></span>
</div>
<div class="form-group">
<label asp-for="Password">Password</label>
<input asp-for="Password" type="password" class="form-control"/>
<span asp-validation-for="Password" class="text-warning"></span>
</div>
<div class="form-group">
<input asp-for="RememberMe" type="checkbox" class="checkbox-inline" />
<label asp-for="RememberMe">Remember me?</label>
<span asp-validation-for="RememberMe" class="text-warning"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Login</button>
</div>
</form>
</div>
</div>
***LoginViewModel
public class LoginViewModel
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
public bool RememberMe { get; set; }
}
9. allow https in website when login
in Startup.cs, in ConfigureServices
services.AddMvc(opt =>
{
if (this.env.IsProduction()) {
opt.Filters.Add(new RequireHttpsAttribute());
}
})
.AddJsonOptions(opt => opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
10. add logout function
in _Layout.cshtml, in menu section, add
@if (User.Identity.IsAuthenticated)
{
<li><a asp-controller="Account" asp-action="Logout">Logout</a></li>
}
else {
<li><a asp-controller="Account" asp-action="Login">Login</a></li>
}
in AccountController, add
[HttpGet]
public async Task<IActionResult> Logout()
{
await this.signInManager.SignOutAsync();
return RedirectToAction("Index", "App");
}