最近認証回りをちょこちょこ調べていて、しばやんさんの記事をベースに .NET 6 でおためし
ASP.NET Core アプリケーションのログアウト時に認証クッキーを確実に無効化する
やりたいこと
- 認証情報を redis に保持する
- ログアウト時に redis の値を削除し認証情報を無効にする
環境
- Visual Studio 2022 / .NET 6
- redis on docker
モジュール構成
Sample.csproj
└ Controllers
└ AccountController.cs
└ ValuesController.cs
└ RedisSessionStore.cs
└ Program.cs
コード
AccountController.cs
public IActionResult Login()
{
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, "test taro"),
new Claim(ClaimTypes.Role, "Administrator")
}, CookieAuthenticationDefaults.AuthenticationScheme));
var authenticationProperties = new AuthenticationProperties
{
IsPersistent = true,
RedirectUri = Url.Action("Index", "Home")
};
return SignIn(claimsPrincipal, authenticationProperties, CookieAuthenticationDefaults.AuthenticationScheme);
}
public IActionResult Logout()
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = Url.Action("Index", "Home")
};
return SignOut(authenticationProperties, CookieAuthenticationDefaults.AuthenticationScheme);
}
- 単純なログイン/ログアウト処理を定義
- 認証情報の取り扱いを確認したいので、固定の Claim を使用する
ValuesController.cs
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public ActionResult Get() => Ok(new { name = User?.Identity?.Name });
}
- こちらも、単純に認証情報を取得するだけの処理を定義
RedisSessionStore.cs
public class RedisSessionStore : ITicketStore
{
private readonly IDistributedCache redisCache;
public RedisSessionStore(IDistributedCache redisCache) => this.redisCache = redisCache;
// 認証情報を保存する
public async Task<string> StoreAsync(AuthenticationTicket ticket)
{
// Guid をキーに認証情報を redis に保存する
var key = Guid.NewGuid().ToString();
await RenewAsync(key, ticket);
return key;
}
// 認証情報を更新する
public async Task RenewAsync(string key, AuthenticationTicket ticket)
{
// 有効期限を 5 分にして保存する
var serializedTicket = TicketSerializer.Default.Serialize(ticket);
await redisCache.SetAsync(key, serializedTicket, new DistributedCacheEntryOptions() { SlidingExpiration = TimeSpan.FromMinutes(5) });
}
// 認証情報を取得する
public async Task<AuthenticationTicket?> RetrieveAsync(string key)
{
// キーに該当する認証情報を取得。なければ null を返す
var serializedTicket = await redisCache.GetAsync(key);
return serializedTicket != null ? TicketSerializer.Default.Deserialize(serializedTicket) : null;
}
// 認証情報を削除する
public async Task RemoveAsync(string key) => await redisCache.RemoveAsync(key);
}
- ITicketStore を実装し、各メソッドで redis の操作を行う
Program.cs
builder.Services.AddSingleton<IDistributedCache, RedisCache>();
builder.Services.AddSingleton<ITicketStore, RedisSessionStore>();
builder.Services.AddStackExchangeRedisCache(options =>
{
// docker で起動した redis を指定する
options.Configuration = "127.0.0.1:6379";
});
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
// こちらも有効期限を 5 分で設定
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
});
builder.Services.AddOptions<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme)
.Configure<ITicketStore>((options, store) => options.SessionStore = store);
- nuget 参照で
Microsoft.Extensions.Caching.StackExchangeRedis
を追加しておく
redis
docker-compose.yml
version: '3'
services:
redis:
container_name: redis
image: redis:latest
ports:
- 6379:6379
volumes:
- ./data/redis:/data
実行
- /Account/Login にアクセス
- Cookie の
.AspNetCore.Cookies
の値をコピー
- Cookie の
- Postman から /api/values にリクエスト
- Key = Cookie / Value = .AspNetCore.Cookies=xxxxxxxxxxxxxxx を設定
-
"name": "test taro"
がレスポンス
- /Account/Logout にアクセス
- Postman から /api/values にリクエスト
- Key = Cookie / Value = .AspNetCore.Cookies=xxxxxxxxxxxxxxx を設定
-
"name": null
がレスポンス