jxycms
5/7/2018 - 10:11 AM

.net core Identity

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");
}