前回の続きです
今回から数回に分けて認証機能を導入します
ガイド
全体Index:タスク管理ツールをReact + ASP.Coreで作る - Index
認証機能追加の一連の流れ
- 016サーバーサイドに認証機能関連のクラス追加(本記事)
- 017クライアントにユーザー登録・ログイン画面と処理を追加
- 018認証トークン関連機能をサーバー・クライアント双方に追加
- 019認証有無に従い制御を実施する機能を追加
- 020認証機能をリファクタリング
- 021認証状態に従い画面表示を切り替え
構築
本記事で実施する内容のサマリー
- パッケージをインストール
- 認証機能にかかわるコードを追加
- ユーザーを管理のためのテーブルの原資になるクラスを追加
- DBに認証ユーザーを管理するテーブルを追加する様に、コンテキストを変更
- 認証関連操作(ログイン・新規登録・認証情報からログイン中のユーザー情報取得)のためのデータモデルを追加
- 認証関連操作の為のコントローラークラスの追加
- webサービス機能の中に認証機能を追加
- マイグレーションしてデータベースを更新
パッケージをインストール
認証機能を有効にするため、以下のパッケージをNugetギャラリーからインストールします
- 認証機能関連
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.AspNetCore.Identity.UI
コード追加・変更
準備ができたら認証関連のコードを追加していきます。
以下のファイルを追加or変更します
ファイル名 | 変更内容 | 概要 |
---|---|---|
ApplicationUser.cs | 新規追加 | ユーザー管理テーブル用モデルクラスを新規追加 |
DataContext.cs | 変更 | コンテキストクラスを認証機能付きに変更 |
AccountController.cs | 新規追加 | ログイン・ユーザー登録処理・結果取得を行うコントローラー |
LoginModel.cs | 新規追加 | ログイン処理時にコントローラーにデータを渡すためのモデルクラス |
RegisterModel.cs | 新規追加 | ユーザー登録時にコントローラーにデータを渡すためのモデルクラス |
UserModel.cs | 新規追加 | ログイン・ユーザー登録後にコントローラーから結果を返す際に使用するクラス |
Startup.cs | 変更 | 認証関連の処理を追加する |
ApplicationUser.cs
ApplicationUser.cs
using Microsoft.AspNetCore.Identity;
namespace server_app.Models.EDM
{
public class ApplicationUser : IdentityUser
{
}
}
DataContext.cs
DataContext.cs
+ using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace server_app.Models.EDM
{
+ public class DataContext : IdentityDbContext<ApplicationUser>
{
+ public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
public DbSet<t_task> t_tasks {get; set;}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
+ base.OnModelCreating(modelBuilder);
modelBuilder.Entity<t_task>(entity =>
{
entity.HasKey(e => new { e.id_task });
});
}
}
}
AccountController.cs
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;
using server_app.Models.EDM;
using server_app.Models.DTO;
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
LoginModel.cs
namespace server_app.Models.DTO
{
public class LoginModel
{
public string Email {get; set;}
public string Password {get; set;}
}
}
RegisterModel.cs
RegisterModel.cs
using System.ComponentModel.DataAnnotations;
namespace server_app.Models.DTO
{
public class RegisterModel
{
[Required]
[EmailAddress]
public string Email { get; set;}
[Required]
[RegularExpression("(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{4,8}$", ErrorMessage ="Password must be complex")]
public string Password {get; set; }
[Required]
public string Username { get; set; }
}
}
UserModel.cs
UserModel.cs
namespace server_app.Models.DTO
{
public class UserModel
{
public string Username {get; set; }
}
}
Startup.cs
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.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.Models.EDM;
namespace server_app
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(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<DataContext>();
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
}));
}
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
// 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();
});
}
}
}
データベース更新
コードの追加が出来たので、データベースを生成します。
以下のコマンドを実行します
dotnet ef migrations add addIdentity
dotnet ef database update
動作
ここまでの流れで認証機能の基本部分を導入しました。
各APIに認証状態に沿ってアクセス制限を行う設定を入れていないので、この時点では動作させてもこれまでと動きに違いはないので、動作は省略します。
続きは次回です
TAG:V000-13-00