Create a mechanism to be redirected if you do not log in using cookie authentication (ASP.NET Core 3.0)
Environment
- Visual Studio
-
- Visual Studio Community 2019
- ASP.NET Core
- 3.0 or later
Cookie authentication can be used in .NET Core 2.2 or earlier, but our tips include code specifically for .NET Core 3.0 and later.
At first
ASP.NET Core uses cookie authentication as a login authentication mechanism. You can think of cookie authentication as something similar to traditional forms authentication.
Another way to authenticate ASP.NET Core is ASP.NET Core Identity. In addition to authentication using forms, api authentication, external login services, password management and reset, etc. You can use a lot of functions. However, from the viewpoint of just making a simple login screen this time, it becomes a mechanism of a little exaggerated authentication I do not use it at this time.
In the tips for cookie authentication, you can not display anything other than the login screen unless you log in. If you try to go to a different screen, you will be redirected to the login screen. You can also see other screens by logging in.
Once you enter your user name and password on the login screen, you can log in. The authentication of the user itself is implemented in a temporary position. Since the main implementation of cookie authentication is to the last this time, the judgment process, such as whether the password is correct, is not essential.
This Tips lists the program spartly. Download the complete program for the complete code.
Create a project
Start Visual Studio and create a new project.
select ASP.NET Core Web Application.
Specify any project name and project location.
This time, we'll use mvc projects, but most of the other templates can be substituted.
For authentication, select No authentication. If you choose other authentication, you will use ASP.NET Core Identity.
After you create a project and debug it, the screen in the figure below appears. We will create a program based on this screen.
Edit Startup.cs
Startup.cs add the definitions required for cookie authentication. Namespaces use:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
You can enable cookie authentication by running the AddAuthentication and AddCookie methods. Specify CookieAuthenticationDefaults.AuthenticationScheme if you do not need to change the scheme name in particular.
The addauthorization method options. If you specify RequireAuthenticatedUser in FallbackPolicy, All controllers can have an authentication-required policy applied to the action. It is also useful in the sense that the code is reduced and the description mistake is prevented if authentication is required other than the login screen. Only the login screen will write the code that does not require authentication individually.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// Cookie による認証スキームを追加する
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
services.AddAuthorization(options =>
{
// AllowAnonymous 属性が指定されていないすべての Action などに対してユーザー認証が必要となる
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
}
Add authentication to your application. Add UseAuthentication(). The description location is based on the MSDN documentation. Place in front of UseAuthorization(). Everything else remains a template.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. ...
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication(); // [追加] 認証
app.UseAuthorization(); // 認可
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Creating an AccountController
Create the controllers and actions you need to create a login screen. The controller name is created as AccountController. This is because by default, the controller name and action name of the login screen are determined to be "~/Account/Login". If you want to change this path, you can change it with the options of the AddCookie method in Startup.cs. We'll go through with the default settings.
The first step is to create the controller side. By granting the AllowAnonymous attribute, all the actions in it can be performed even if they are not authenticated. This allows only the login screen to be accessed without authentication.
The AllowAnonymous attribute is independent of cookie authentication as a location other than the login screen. It may also be used on api-only controllers.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
namespace CookieAuthentication.Controllers
{
<remarks>
<see cref="AllowAnonymous"/> 属性は Cookie 認証していなくてもアクセスできる Action (Controller) であることを示す。
</remarks>
[AllowAnonymous]
public class AccountController : Controller
{
}
}
Define the users and passwords that can be logged in. Originally i save it to a database, but this time the user judgment is not the main focus I make it by a temporary set.
<summary>仮のユーザーデータベースとする。</summary>
private Dictionary<string, string> UserAccounts { get; set; }
public AccountController()
{
// 仮のユーザーを登録する
UserAccounts = new Dictionary<string, string>
{
{ "user1", "password1" },
{ "user2", "password2" },
};
}
This is an action to display the login screen. Return the view as it is because it only displays.
<summary>ログイン画面を表示します。</summary>
public IActionResult Login()
{
return View();
}
Authentication process after pressing the login button. If the user name and password match, it can be authenticated.
The following code defines claim, identity, and Pricinpal as the minimum code required for authentication. Calling the HttpContext.SignInAsync method generates a cookie and authenticates it.
If additional claims are required or cookies expire, add parameters.
After logging in, we have redirected to ~/Home/Index, which requires authentication.
<summary>ログイン処理を実行します。</summary>
[HttpPost]
public async Task<IActionResult> Login(string userName, string password)
{
// ユーザーの存在チェックとパスワードチェック (仮実装)
// 本 Tips は Cookie 認証ができるかどうかの確認であるため入力内容やパスワードの厳密なチェックは行っていません
if (UserAccounts.TryGetValue(userName, out string getPass) == false || password != getPass)
{
return View();
}
// サインインに必要なプリンシパルを作る
var claims = new[] { new Claim(ClaimTypes.Name, userName) };
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
// 認証クッキーをレスポンスに追加
await HttpContext.SignInAsync(principal);
// ログインが必要な画面にリダイレクトします
return RedirectToAction(nameof(HomeController.Index), "Home");
}
Enter the logout process. You can delete a cookie and return it to an unlogged state by calling the HttpContext.SignOutAsync method.
<summary>ログアウト処理を実行します。</summary>
public async Task<IActionResult> Logout()
{
// 認証クッキーをレスポンスから削除
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// ログイン画面にリダイレクト
return RedirectToAction(nameof(Login));
}
Create a view (login form) (/Views/Account/Login.cshtml)
Since we don't take it into account, add a field to enter your username and password, as shown in the figure below, and place a button to log in. You should also place a link to access Home/Index without logging in for testing purposes.
Because it is troublesome to enter the user name and password, the initial value is set.
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
</head>
<body>
<form method="post">
<input name="userName" type="text" placeholder="ユーザー名" value="user1" />
<input name="password" type="password" placeholder="パスワード" value="password1" />
<button type="submit">ログイン</button>
</form>
<p><a asp-controller="Home" asp-action="Index">認証が必要な画面へ直接リンク</a></p>
</body>
</html>
Create a logout link (/Views/Shared/_Layout.cshtml)
The home screen won't change anything, but you'll have a logout link in the navigation bar. In addition, i'll also put a link that transitions to the login screen without logging out for testing purposes.
<!-- 中略 -->
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
<!-- ここから追加 -->
<li class="nav-item">
<a class="nav-link text-dark" asp-controller="Account" asp-action="Logout">Logout</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-controller="Account" asp-action="Login">ログアウトせずログインへ</a>
</li>
<!-- ここまで追加 -->
</ul>
</div>
<!-- 中略 -->
Check operation
You have now completed the minimum implementation required for cookie authentication. Try to do it and see how it works. It should change the behavior depending on whether you are logged in or not. As a simple example, i think you can see the following behavior.
Operation | ⇒ | Operation Results |
---|---|---|
Go home without logging in | ⇒ | Redirect to login screen |
Login | ⇒ | Go to Home Screen |
Log out of home and go home without logging in | ⇒ | Redirect to login screen |
Don't log out of home and go home without logging in | ⇒ | Go to Home Screen |