🎉
BlazorでWindows認証と独自認証を組み合わせる
やりたいこと
- Blazor(Server)でWindows認証を使用したうえで、認証・承認は独自で行いたい。
前提
前回作ったやつの続きを想定。
読みにくいので気が向いたら推敲する(しない)。
作業手順
以下の手順に従う。
認証・承認用のDBを追加
今回はローカルでPostgreSQLを用意し、そこにユーザーの情報を入れることにする。
- PostgreSQLをインストール
- PostgreSQL 16.3
https://www.enterprisedb.com/downloads/postgres-postgresql-downloads
インストール手順は全部Next。パスワードも適当。
- インストールできたらpgadminを開いて確認。
Blazor側
EntityFrameworkとPostgreSQLを利用する。
- NuGetで必要なパッケージをインストールする。
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.Tools
- Npgsql.EntityFrameworkCore.PostgreSQL
- 適当なフォルダを作りモデルを追加する。
User.cs
//using省略
namespace Windows認証テスト.Model
{
[Table("users"),Comment("ログイン管理用")]
public class User
{
[Column("user_id"), Comment("ユーザーID")]
[Key]
public string Id { get; set; }
/// <summary>
/// 権限 0:一般ユーザ 1: 管理者の想定
/// </summary>
[Column("role"),Comment("権限コード")]
public int Role { get; set; }
}
}
- 適当なフォルダを作りDbContextを追加する。
TestDbContext.cs
//using省略
namespace Windows認証テスト.DB
{
public class TestDBContext : DbContext
{
public TestDBContext(DbContextOptions<TestDBContext> options) : base(options)
{
}
public DbSet<User> Users{ get; set; }
}
}
-
Program.cs
とappsettings.json
を編集して接続情報を追加する。
Prgram.cs
+builder.Services.AddDbContextFactory<TestDBContext>(opt => {
+ opt.UseNpgsql(
+ builder.Configuration.GetConnectionString("DefaultConnection"),
+ providerOptions =>
+ {
+ providerOptions.EnableRetryOnFailure();
+ });
+});
appsettings.json
+ "ConnectionStrings":
+ {"DefaultConnection":"Host=localhost:5432;Database=testdb;Username=postgres;Password=パスワード" }
実際にはセキュリティ的に環境変数とかで設定したほうが?
- コードからテーブルを作る。
PowerShellから
dotnet ef migrations add InitialCreate
を実行すると、Migrationsフォルダができる。
dotnet ef database update
でデータベースとテーブルが作られる。
Role認証のあれこれ
- ロール情報の追加
Windows認証ができたらDBからロール情報を取得しセットする。
Program.csに以下を追加し、独自のClaimsTransformation.csを作成する。
builder.Services.AddDbContextFactory<TestDBContext>(opt =>
{
opt.UseNpgsql(
builder.Configuration.GetConnectionString("DefaultConnection"),
providerOptions =>
{
providerOptions.EnableRetryOnFailure();
});
});
+ builder.Services.AddScoped<IClaimsTransformation,CustomClaimsTransformation>();
適当な場所に
CustomClaimsTransformation.cs
//using省略
namespace Windows認証テスト
{
public class CustomClaimsTransformation : IClaimsTransformation
{
private readonly IDbContextFactory<TestDBContext> _dbFactory;
public CustomClaimsTransformation(IDbContextFactory<TestDBContext> dbFactory)
{
_dbFactory = dbFactory;
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var identity = (ClaimsIdentity)principal.Identity;
var newIdentity = new ClaimsIdentity(identity.Claims, identity.AuthenticationType);
//DBから取得したロール情報を追加
using (var db = _dbFactory.CreateDbContext())
{
var user = db.Users.FirstOrDefault(u => u.UserId == identity.Name);
if (user != null)
{
//とりあえず1ならAdmin、それ以外はUserとする
// identity.AddClaim(new Claim(ClaimTypes.Role, user.Role == 1 ? "Admin" : "User"));
newIdentity.AddClaim(new Claim(ClaimTypes.Role, user.Role == 1 ? "Admin" : "User"));
}
}
var newPrincipal = new ClaimsPrincipal(newIdentity);
principal.AddIdentity(newIdentity);
return Task.FromResult(principal);
}
}
}
試行錯誤メモ
AddNegotiateのoptionを設定すればこの辺が不要になりそうだけど、このoptionが機能するのはkestrelのみっぽい。
発行するど動作が変わる(エラーにはならない)ので大パニック。- Home.razorでロールが正しく機能するか確認。
Home.razor
<AuthorizeView>
<p>ログインユーザ: @context.User.Identity.Name!</p>
</AuthorizeView>
<AuthorizeView Roles="User">
<p>Role:User</p>
</AuthorizeView>
<AuthorizeView Roles="Admin">
<p>Role:Admin </p>
</AuthorizeView>
実際にアクセスしてみる。
よさそう。
参考サイト
Discussion