加上簡單的登入驗證 For MVC
環境
- Rider 產生的.NetCore MVC的範本專案
- .NetCore 3.1
1.準備了登入用的View、Action、ViewModel
// MemberController
using EasyIdentitySample.ViewModel;
using Microsoft.AspNetCore.Mvc;
namespace EasyIdentitySample.Controllers
{
public class MemberController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(MemberViewModel request)
{
if (request.Account == "Test" && request.Password == "123")
{
// TODO SignIn
return RedirectToAction("Index", "Home");
}
return View();
}
[HttpPost]
public IActionResult SignOut()
{
// TODO SignOut
return RedirectToAction("Index");
}
}
}
// MemberViewModel
namespace EasyIdentitySample.ViewModel
{
public class MemberViewModel
{
public string Account { get; set; }
public string Password { get; set; }
}
}
<!--Index.cshtml-->
@model EasyIdentitySample.ViewModel.MemberViewModel
@{
ViewBag.Title = "title";
Layout = "_Layout";
}
<h2>Login</h2>
<form asp-action="Index" asp-controller="Member" method="post">
<label asp-for="Account">
<input asp-for="Account">
</label>
<br/>
<label asp-for="Password">
<input asp-for="Password">
</label>
<br>
<button>Submit</button>
</form>
2.調整Startup,加上Authentication
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie();
services.AddHttpContextAccessor();
}
// Configure
app.UseAuthentication();
4.在登入的Action,預計會使用 HttpContext.SignInAsync()來做登入,這邊先把會用到的參數都展開出來
- 會使用 IHttpContextAccessor 來訪問HttpContext(需要在Startup加上 services.AddHttpContextAccessor())
[HttpPost]
public async Task<IActionResult> Index(MemberViewModel request)
{
if (request.Account == "Test" && request.Password == "123")
{
var claims = new List<Claim>();
var claimsIdentity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
await _accessor.HttpContext.SignInAsync(claimsPrincipal);
return RedirectToAction("Index", "Home");
}
return View();
}
5.接著調整下登入Action,使用 GenericIdentity 作為身份驗證的物件
[HttpPost]
public async Task<IActionResult> Index(MemberViewModel request)
{
if (request.Account == "Test" && request.Password == "123")
{
var claimsIdentity = new GenericIdentity(ClaimTypes.Name,request.Account);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
await _accessor.HttpContext.SignInAsync(claimsPrincipal);
return RedirectToAction("Index", "Home");
}
return View();
}
6.在其他Action加上登入檢查
public IActionResult Index()
{
if (_accessor.HttpContext.User.Identity.IsAuthenticated==false)
{
return RedirectToAction("Index", "Member");
}
return View();
}
7.登入後,在 HttpContext.User.Identity.IsAuthenticated 理應就可以拿到 true了
8.接著回到登入Action,這次我們改用 ClaimsIdentity,並且加上一個Role
-
GenericIdentity是繼承於ClaimsIdentity
[HttpPost]
public async Task<IActionResult> Index(MemberViewModel request)
{
if (request.Account == "Test" && request.Password == "123")
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, request.Account),
new Claim(ClaimTypes.Role, "Admin"),
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
await _accessor.HttpContext.SignInAsync(claimsPrincipal);
return RedirectToAction("Index", "Home");
}
return View();
}
9.再回到其他Action,可以正常地取得登入使用者的Role
public IActionResult Index()
{
if (_accessor.HttpContext.User.Identity.IsAuthenticated == false)
{
return RedirectToAction("Index", "Member");
}
var isInRole = _accessor.HttpContext.User.IsInRole("Admin");
return View();
}
10.接著繼續完成登出的功能,只需要加上一行程式碼
[HttpPost]
public async Task<IActionResult> SignOut()
{
await _accessor.HttpContext.SignOutAsync();
return RedirectToAction("Index");
}
11.這邊為止,登出登入功能也都完成了,接著再調整下程式碼
12.把登入檢查改用 AuthorizeAttribute
- 未登入時預設會導向/Login
- 並且加入一個QueryString的參數"ReturnUrl",代表導向登入頁前的網址
[Authorize]
public IActionResult Index()
{
var isInRole = _accessor.HttpContext.User.IsInRole("Admin");
return View();
}
13.登入預設頁面可以在Startup上設定
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/Member/Index";
});
services.AddHttpContextAccessor();
}
14.接著調整下登入Action及View,讓ReturnUrl有作用
- 多個檢查判斷,確定是Local Url才導向,否則導向固定頁面
// Login
[HttpPost]
public async Task<IActionResult> Index(MemberViewModel request,string returnUrl)
{
if (request.Account == "Test" && request.Password == "123")
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, request.Account),
new Claim(ClaimTypes.Role, "Admin"),
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
await _accessor.HttpContext.SignInAsync(claimsPrincipal);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
return View();
}
@model EasyIdentitySample.ViewModel.MemberViewModel
@{
ViewBag.Title = "title";
Layout = "_Layout";
var returnUrl = Context.Request.Query["ReturnUrl"];
}
<h2>Login</h2>
<form asp-action="Index" asp-controller="Member" method="post" asp-route-returnUrl="@returnUrl">
<label asp-for="Account">
<input asp-for="Account">
</label>
<br/>
<label asp-for="Password">
<input asp-for="Password">
</label>
<br>
<button>Submit</button>
</form>
15.這樣若需要檢查登入,僅需要加上Attribute即可,使用者登入後也能直接導向回欲進入的頁面
微軟Docs https://docs.microsoft.com/zh-tw/aspnet/core/security/authentication/cookie?view=aspnetcore-3.1
Sample Code https://github.com/ianChen806/EasyIdentitySample