ASP.NET Core MVC 上的 Cookie Authentication

在 ASP.NET Core MVC 上要做登入功能,或許會先想到 ASP.NET Core Identity,但它的複雜性實在太高,所以微軟在 ASP.NET Core MVC 上還提供另一種登入方式,就是單純以 Cookie 做認證。

在 Startup.cs 設定

首在開啟 Startup.cs,啟用 cookie 認證功能:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options =>
        {
            options.LoginPath = "/Home/Login";
        });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

ConfigureServices 函式中的 options.LoginPath = "/Home/Login"; 這一行是設定登入頁面的網址,預設值是 /Account/Login。接下來的 Configure 函式中的 app.UseAuthentication(); 這一行,務必要在 app.UserMvc 前就呼叫。

Model 設計

接下來是 model 的設計,在 Models 資料夾中新增一個 UserModel.cs 檔案,並在裡面新增類別:

public class UserModel
{
    public UserModel()
    {

    }

    public static UserModel Login(string account, string password)
    {
        UserModel user = null;

        if (account == "admin" && password == "yourpwd")
        {
            user = new UserModel();
            user.Email = "admin@domain.com";
            user.UserName = "admin";
            user.UserId = "admin";
        }

        return user;
    }

    public string Email { get; set; }
    public string UserName { get; set; }
    public string UserId { get; set; }
}

UserModel 裡面除了一些使用者的個人資料,裡面還多了個 static 函式:Login,主要是用來做認證用,為了簡化所以我把帳密寫死在裡面,這部份的邏輯可以自己自由抽換。

另外,在登入時因為需要用到 controller 底的 HttpContext 屬性,所以索性就弄了個擴充方法:

public static class ControllerExtensionClass
{
    public async static void SetUser(this Controller controller, UserModel user)
    {
        var claims = new List<Claim>();
        claims.Add(new Claim("email", user.Email));
        claims.Add(new Claim(nameof(user.UserId), user.UserId));
        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

        await controller.HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
            new ClaimsPrincipal(claimsIdentity),
            new AuthenticationProperties
            {
                IsPersistent = true
            });
    }
}

Controller 部份

而在 controller 部份,Login 部份的程式碼如下:

[HttpGet]
public IActionResult Login()
{
    return View();
}

[HttpPost]
public IActionResult Login(UserViewModel userModel)
{
    string returnUrl = @"/Home/Permission";
    UserModel user = UserModel.Login(userModel.Account, userModel.Password);
    if (user != null)
    {
        this.SetUser(user);
        return Redirect(returnUrl);
    }
    else
    {
        return View();
    }
}

Login 的部份利用了 UserViewModel 來當做參數,程式碼如下:

public class UserViewModel
{
    public UserViewModel()
    {

    }

    public string Account { get; set; }
    public string Password { get; set; }
}

會切開來的原因,在於我覺得 View 使用到的類別跟包含商業邏輯的 Model 分開,在其中一方做修改時才不會相互影響。

最後來看 View 部份,Login.cshtml:

@model CookieAuthenticationDemo.Models.ViewModels.UserViewModel
@{
    ViewData["Title"] = "Login";
}
<h2>Login</h2>
<h4>UserViewModel</h4>
<hr />
<div class="row">
    <div class="col-md-4">

            <div class="text-danger"></div>
            <div class="form-group">


                <span class="text-danger"></span>
            </div>
            <div class="form-group">


                <span class="text-danger"></span>
            </div>
            <div class="form-group">

            </div>
        </form>
    </div>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

全部都完成後,controller 中需要認證的 action 只要掛上 Authorize 這個 attribute 就可以了,像是這樣:

[Authorize]
public IActionResult Permission()
{
    var claims = HttpContext.User.Claims;
    string content = string.Empty;

    foreach (var claim in claims)
    {
        content += claim.Value + " ";
    }

    return Content(content);
}

範例參考

參考資料

廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

w

連結到 %s