説明
権限認証とは、ユーザーがリソースにアクセスする権限を持っているかを判断するために、ユーザーの身元を確認するプロセスです。
今回はJWTのようなトークンベースの認証メカニズムを紹介します。
トークンベースの認証メカニズムでは、サーバー側にユーザーの認証情報やセッション情報を保持する必要がありません。つまり、トークンベースの認証を採用したアプリケーションは、ユーザーがどのサーバーにログインしたかを考慮する必要がなく、アプリケーションの拡張が容易になります。
フローは以下の通りです。
- ユーザーはユーザー名とパスワードを使用してサーバーにリクエストを送信します。
- サーバーはユーザー情報を検証します。
- サーバーは検証後、ユーザーにトークンを送信します。
- クライアントはトークンを保存し、リクエストのたびにこのトークン値を添付します。
- サーバーはトークン値を検証し、データを返します。
それでは、本日はJWTのような権限認証を手作りしてみます。
デモ
新しい認可フィルターを作成し、IAuthorizationFilter を実装します
public class ApiAuthorize : IAuthorizationFilter
{}
認可が必要なことを示す属性と、認証不要でアクセスを許可する属性を作成します
public class MyAuthentication:Attribute, IFilterMetadata
{
}
public class MyNoAuthentication : Attribute, IFilterMetadata
{
}
認可フィルターでは、リクエストヘッダーに認可が必要な属性や認証不要でアクセスを許可する属性が付与されているかを判定します。認証不要属性があればそのまま次のパイプラインへ進み、認可が必要な属性があればトークンの検証を行います。
public class ApiAuthorize : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context.Filters.Contains(new MyNoAuthentication()))
{
return;
}
var authorize = context.HttpContext.Request.Headers["MyAuthentication"];
if (string.IsNullOrWhiteSpace(authorize))
{
context.Result = new JsonResult("リクエストのauthorizeが空です");
return;
}
if (!MemoryCacheHelper.Exists(authorize))
{
context.Result = new JsonResult("無効な認証情報、または認証情報が期限切れです");
return;
}
}
}
気づいた方もいるかもしれませんが、トークンの保存と取得はどうするのでしょうか?一般的な方法としてはCacheを使用しますが、ここでは詳しく説明しません。興味がある方は前回の記事をご参照ください!
CacheHelper のコード
public class MemoryCacheHelper
{
public static MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
/// <summary>
/// キャッシュアイテムが存在するか検証します
/// </summary>
/// <param name="key">キャッシュキー</param>
/// <returns></returns>
public static bool Exists(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return _cache.TryGetValue(key, out _);
}
/// <summary>
/// キャッシュを取得します
/// </summary>
/// <param name="key">キャッシュキー</param>
/// <returns></returns>
public static object Get(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (!Exists(key))
throw new ArgumentNullException(nameof(key));
return _cache.Get(key);
}
/// <summary>
/// キャッシュを追加します
/// </summary>
/// <param name="key">キャッシュキー</param>
/// <param name="value">キャッシュ値</param>
/// <param name="expiresSliding">スライディング有効期限(有効期限内に操作があった場合、現在時刻から有効期限を延長します)</param>
/// <param name="expiressAbsoulte">絶対有効期限</param>
/// <returns></returns>
public static bool AddMemoryCache(string key, object value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_cache.Set(key, value,
new MemoryCacheEntryOptions()
{
SlidingExpiration = new TimeSpan(0, 0, 10),
Priority = CacheItemPriority.NeverRemove,
AbsoluteExpiration = DateTime.Now.AddMinutes(1)
});
return Exists(key);
}
}
権限認証のコードはほぼ完了しました。先ほどのフローに戻ります。
ユーザーがユーザー名とパスワードでサーバーにリクエストし、サーバーがユーザー情報を検証し、検証成功後にトークンをユーザーに送信します。
商用ソフトウェアでは、ほとんどのインターフェースで認可が必要です。そのため、グローバルに登録します。StartUp クラスで登録します。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(o=>
{
o.Filters.Add<ApiAuthorize>();
o.Filters.Add<MyAuthentication>();
});
}
トークン生成の原理についてはここでは詳しく解説しません。この例ではAES暗号化を使用してトークンを生成します。サーバー側のトークン要求インターフェースに認証不要属性を付与し、トークンを発行します。
[HttpGet("GetToken")]
[MyNoAuthentication]
public IActionResult GetToken(string UserCode)
{
string token= AESEncrypt.Encrypt(UserCode);
MemoryCacheHelper.AddMemoryCache(token, User);
return Ok(token);
}
Postmanでトークンを生成するリクエストを送信します。画像の通りです。

次に、認可が必要なインターフェースを新規追加します。すでにグローバルで認可属性を登録しているため、このインターフェースに改めて属性を付ける必要はありません。
[HttpGet("GetUserInformation")]
public IActionResult GetUserInformation()
{
return Ok(new { Name="123",Age=18,Sex="性別"});
}
トークンなしでPostmanからこのインターフェースにリクエストを送信します。画像の通りです。

トークンを付けてPostmanからこのインターフェースにリクエストを送信します。画像の通りです。

以上の例で、権限認証のプロセスを明確に理解していただけたと思います。本日の紹介はここまでです。
最後に、私の記事を気に入っていただけましたら、フォローといいねをお願いします。.NETエコシステムがますます良くなることを願っています!