ASP.NET Core + React + Typescriptで認証付きWebアプリのひな型を作る③サーバーサイドに認証機能を追加する
概要
前回までで、サーバーサイドの情報を受け取ってクライアントサイドで表示することができるようになりました。最低限の動作が確認できるようになったので、認証機能を追加していこうと思います。
認証機能追加の流れは以下です
- 環境確認・追加
- 必要なパッケージのインストール
環境確認・追加
Extensionsを開きます
↓以下2パッケージが入っている事を確認して、なければインストールしておきます
Nuget GalleryはNugetパッケージをインストールする際に、SQLiteはSQLiteのデータを確認するために使用します。SQLiteはインストールしなくてもアプリの構築自体は可能ですが、データを閲覧したい際などに便利です
必要なパッケージのインストール
拡張機能の設定後は、必要なパッケージをインストールしていきます。
F1を押下して、Nugetパッケージマネージャーを開きます
パッケージをインストールします。
インストールするパッケージは、以下です
- 認証機能
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.AspNetCore.Identity.UI
- EntityFramework関連
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.Sqlite
インストールできているかの確認方法はいろいろありますが、↓のようにプロジェクトファイルを見てみて上記のパッケージが入っていればOKです。
コード追加・変更
準備ができたら認証関連のコードを追加していきます。
以下のファイルを追加or変更します
ファイル名 | 変更内容 | 役割 |
---|---|---|
ApplicationUser.cs | 新規追加 | ユーザー管理テーブル用モデルクラス |
ApplicationDbContext.cs | 新規追加 | データ(今回の場合はSQLite)を操作するためのコンテキストクラス |
AccountController.cs | 新規追加 | ログイン・ユーザー登録処理・結果取得を行うコントローラー |
LoginModel.cs | 新規追加 | ログイン処理時にコントローラーにデータを渡すためのモデルクラス |
RegisterModel.cs | 新規追加 | ユーザー登録時にコントローラーにデータを渡すためのモデルクラス |
UserModel.cs | 新規追加 | ログイン・ユーザー登録後にコントローラーから結果を返す際に使用するクラス |
Startup.cs | 変更 | 認証関連の処理を追加する |
appsettings.json | 変更 | 接続文字列を追加する |
ApplicationUser.cs
using Microsoft.AspNetCore.Identity;
namespace server_app.Models
{
public class ApplicationUser : IdentityUser
{
}
}
ApplicationDbContext.cs
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using server_app.Models;
namespace server_app.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
}
AccountController.cs
using System.Security.Claims;
using System.Threading.Tasks;
using server_app.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace server_app.Controllers
{
[AllowAnonymous]
[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signinManager;
public AccountController(UserManager<ApplicationUser> userManager
,SignInManager<ApplicationUser> signinManager
)
{
_signinManager = signinManager;
_userManager = userManager;
}
[HttpPost("login")]
public async Task<ActionResult<UserModel>> Login(LoginModel loginModel)
{
var user = await _userManager.FindByEmailAsync(loginModel.Email);
if(user == null) return Unauthorized();
var result = await _signinManager.CheckPasswordSignInAsync(user, loginModel.Password, false);
if(result.Succeeded)
{
return CreateUserObject(user);
}
return Unauthorized();
}
[HttpPost("register")]
public async Task<ActionResult<UserModel>> Register(RegisterModel registerModel)
{
if(await _userManager.Users.AnyAsync(x => x.Email == registerModel.Email))
{
ModelState.AddModelError("email", "Email taken");
return ValidationProblem();
}
if(await _userManager.Users.AnyAsync(x => x.UserName == registerModel.Username))
{
ModelState.AddModelError("username", "Username taken");
return ValidationProblem();
}
var user = new ApplicationUser
{
Email = registerModel.Email,
UserName = registerModel.Username
};
var result = await _userManager.CreateAsync(user, registerModel.Password);
if(result.Succeeded)
{
return CreateUserObject(user);
}
return BadRequest("Problem regist User");
}
private UserModel CreateUserObject(ApplicationUser user)
{
return new UserModel
{
Username = user.UserName
};
}
}
}
LoginModel.cs
namespace server_app.Models
{
public class LoginModel
{
public string Email {get; set;}
public string Password {get; set;}
}
}
RegisterModel.cs
using System.ComponentModel.DataAnnotations;
namespace server_app.Models
{
public class RegisterModel
{
[Required]
[EmailAddress]
public string Email { get; set;}
[Required]
[RegularExpression("^[a-zA-Z0-9.?/-]{8,24}$", ErrorMessage ="Password must be complex")]
public string Password {get; set; }
[Required]
public string Username { get; set; }
}
}
UserModel.cs
namespace server_app.Models
{
public class UserModel
{
//public string Token { get; set; }
public string Username {get; set; }
}
}
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
+using server_app.Data;
+using server_app.Models;
namespace server_app
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
+ services.AddDbContext<ApplicationDbContext>(opt =>
+ {
+ opt.UseSqlite(Configuration.GetConnectionString("DefaultConnection"));
+ } );
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "server_app", Version = "v1" });
});
+ services.AddDefaultIdentity<ApplicationUser>(
+ options => {
+ options.SignIn.RequireConfirmedAccount = false;
+ }
+ )
+ .AddEntityFrameworkStores<ApplicationDbContext>();
services.AddCors(o => o.AddPolicy(MyAllowSpecificOrigins, builder =>
{
builder.AllowAnyOrigin() // Allow CORS Recest from all Origin
.AllowAnyMethod() // Allow All Http method
.AllowAnyHeader(); // Allow All request header
}));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "server_app v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins); // Add For CORS
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
appsettings.json
接続文字列を追加します
{
+ "ConnectionStrings": {
+ "DefaultConnection":"Data Source=database.db"
+ },
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
データベース更新
コードの追加が出来たので、データベースを生成します。
以下のコマンドを実行します
dotnet ef migrations add InitialCreate
dotnet ef database update
成功すると以下のようなメッセージが表示されます
以下の様にDBが生成されます
サーバーサイドで、「ユーザー登録」と、「ID・パスワードを入力するとOK/NGを返す」機能の実装が出来ました。サーバーサイドだけを見ても、ここまでの内容では「認証機能」としてはまだ不完全ですが、クライアントからアクセスして操作するための最低限の機能が出来たので、次回はクライアントを更新してクライアント側から「ユーザー登録」・「ログイン結果の取得」機能を実装していきます。
Author And Source
この問題について(ASP.NET Core + React + Typescriptで認証付きWebアプリのひな型を作る③サーバーサイドに認証機能を追加する), 我々は、より多くの情報をここで見つけました https://zenn.dev/mosuma/articles/fb8cd6aa69e275著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Collection and Share based on the CC protocol