[VB.NET] REST API⑮ BASIC認証

2021年10月7日

ASP.NET MVC ビューに複数のパラメーターを渡すで作成したサンプルを改造して、Web APIにBASIC認証の機能を実装してみましょう。
※ VisualStudio2019

認証をするための属性クラスを追加

BASIC認証をするための属性クラスを追加していきます。

このサンプルでは独自のフォルダを作って、その中に属性クラスを作成しています。
「App_Code」フォルダを使うといろいろ面倒なので 独自のフォルダ にしています。
ソリューションエクスプローラでプロジェクトを右クリックして、「追加」>「新しいフォルダー」をクリックします。

追加したフォルダーに名前を付けます。
このサンプルでは「Authorize」にしました。

ソリューションエクスプローラで、 追加した「Authorize」フォルダを右クリックして、「追加」>「クラス」をクリックします。

クラス名を入力し、「追加」をクリックします。
このサンプルでは、「UserAuthorizeAttribute」という名前にしています。

「UserAuthorizeAttribute」クラスを実装します。
このクラスは「AuthorizationFilterAttribute」を継承し、「OnAuthorization」メソッドをオーバーライドします。
この中でカスタム認証をおこない、最後に継承元のOnAuthorizationを呼び出します。

このサンプルではユーザー/パスワードは固定で「user1」/「password1」としています。
認証が失敗した場合は、エラーメッセージを返すようにしています。

コード

Imports System.Net
Imports System.Net.Http

Public Class UserAuthorizeAttribute
    Inherits System.Web.Http.Filters.AuthorizationFilterAttribute

    Public Overrides Sub OnAuthorization(actionContext As System.Web.Http.Controllers.HttpActionContext)

        'カスタム認証を行う

        'リクエスト情報を取得
        Dim req As HttpRequestMessage
        req = actionContext.Request

        'リクエストヘッダーから認証情報を取得する
        Dim auth As Headers.AuthenticationHeaderValue
        auth = req.Headers.Authorization


        '認証情報がNothing、またはスキームがbasic認証以外、またはパラメータが無いならエラー応答
        If (auth Is Nothing) OrElse (auth.Scheme.ToLower <> "basic") OrElse (String.IsNullOrEmpty(auth.Parameter)) Then
            actionContext.Response = req.CreateErrorResponse(HttpStatusCode.Unauthorized, "The authentication information is invalid")

        Else
            '認証情報を取り出す
            Dim auth_param As String
            auth_param = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(auth.Parameter))

            'ユーザー名「user1」、パスワードが「password1」の組み合わせ以外はエラー応答
            If auth_param <> "user1:password1" Then
                actionContext.Response = req.CreateErrorResponse(HttpStatusCode.Unauthorized, "The authentication information is invalid")
            End If
        End If

        '継承元のメソッド呼び出し
        MyBase.OnAuthorization(actionContext)

    End Sub
End Class

APIのコントローラーに認証機能をつける

APIのコントローラー(ApiTestController)に、認証機能をつけます。
やり方は簡単で、

<UserAuthorizeAttribute>
Public Class ApiTestController
    Inherits ApiController
・・・

のように、属性を指定するだけです。

ビューのコントローラからAPIを呼び出し

続いてビューのコントローラー(ViewTestController)から、BASIC認証付きでAPIを呼び出し部分を実装します。
実装しているデフォルトのアクションは「Index」だけです。
ユーザー/パスワードは固定で「user1」/「password1」 とし、Base64でエンコードした文字列をヘッダーの「Authorization」にセットしています。
APIのGETが成功した場合と失敗した場合でそれぞれメッセージを表示しています。

コード

Imports System.Web.Mvc

Namespace Controllers
    Public Class ViewTestController
        Inherits Controller

        ' GET: ViewTest
        Function Index() As ActionResult

            '一覧を取得するAPIのURL
            Dim url As String
            url = DirectCast(Request, System.Web.HttpRequestWrapper).Url.GetLeftPart(UriPartial.Authority) & "/api/ApiTest"


            Dim clt As New Net.Http.HttpClient
            Dim result As New System.Net.Http.HttpResponseMessage
            Dim bytes As Byte()
            Dim auth_param As String

            'ユーザー名「user1」、パスワード「password1」の認証情報をバイト配列に変換
            bytes = System.Text.Encoding.UTF8.GetBytes("user1:password1")

            'base64に変換
            auth_param = System.Convert.ToBase64String(bytes)

            'ヘッダーに認証情報を入れる
            clt.DefaultRequestHeaders.Authorization = New System.Net.Http.Headers.AuthenticationHeaderValue("basic", auth_param)

            'GETを実行
            result = clt.GetAsync(url).Result

            Dim model As New ViewTestParam

            If (result.StatusCode = Net.HttpStatusCode.OK) Then
                'Jsonを取り出し
                Dim json As String
                json = result.Content.ReadAsStringAsync.Result

                'デシリアライズ
                Dim rec_list As List(Of Record)
                rec_list = Newtonsoft.Json.JsonConvert.DeserializeObject(Of List(Of Record))(json)

                'モデル作成
                model.Item1 = rec_list
                model.Item2 = "データを取得しました!"
            Else

                'モデル作成
                model.Item1 = New List(Of Record)
                model.Item2 = "データの取得に失敗しました!" & result.Content.ReadAsStringAsync.Result
            End If

            Return View(model)
        End Function
    End Class
End Namespace

ビュー(index.vbhtml)は変更なしです。

@ModelType ViewTestParam
@Code
    ViewData("Title") = "index"
End Code

<h2>index</h2>


@Html.Label("message_label", Model.Item2, New With {.style = "color:red;"})



<table class="table">
    <tr>
        <th>
            @*最初の要素が無くてもエラーにならないので心配しなくてOK*@
            @Html.DisplayNameFor(Function(model) model.Item1.First().col1)
        </th>
        <th>
            @Html.DisplayNameFor(Function(model) model.Item1.First().col2)
        </th>
        <th>
            @Html.DisplayNameFor(Function(model) model.Item1.First().col3)
        </th>
        <th></th>
    </tr>

    @For Each item In Model.Item1
        @<tr>
            <td>
                @Html.DisplayFor(Function(modelItem) item.col1)
            </td>
            <td>
                @Html.DisplayFor(Function(modelItem) item.col2)
            </td>
            <td>
                @Html.DisplayFor(Function(modelItem) item.col3)
            </td>
            <td>
                @*@Html.ActionLink("Edit", "Edit", New With {.id = item.PrimaryKey}) |
                    @Html.ActionLink("Details", "Details", New With {.id = item.PrimaryKey}) |
                    @Html.ActionLink("Delete", "Delete", New With {.id = item.PrimaryKey})*@
            </td>
        </tr>
    Next

</table>

実行して確認する

さっそく実行してみましょう。実行方法はこちらを参照してください。
まずはブラウザで直接APIのGETを呼び出してみます。
ユーザー/パスワードで認証をしていないので、エラーメッセージが表示されるようになりました。

次に、ビューから呼び出してみます。
アドレスバーに「/ViewTest/index」と入力してEnterキーを押してページを移動します。
正しいユーザー/パスワードが設定されているので、認証が成功してデータが取得できます。

続いて、パスワードを少し変えて、わざと間違ったパスワードを送ってみます。
このサンプルでは、「password1」→「NG_password1」にしています。

再度実行してみましょう。
認証エラーのメッセージが表示され、データの取得が失敗したので、認証が機能していることが確認できました。

WEB APIにBASIC認証機能を実装してみました。
イントラ内のシステムで、ちょっとした認証をつけたい場合なんかに利用できます。